[COCI2020-2021#3] Specijacija 题解
Larunatrecy · · 题解
提供一个分块的做法。
考虑把每一个点表示成
同理可以把
考虑如果点
number-=(a[i]<number).
考虑对
那么借助
于是只需求出
考虑增量计算,由
设使得
考虑怎么求出新增加的关键点,考虑设
那么一个位置可以成为关键点,当且仅当
那么新增加的就是最靠右的一个符合这个条件的位置。
这个条件也就是:
用一个线段树维护
那么显然只需要支持区间加就可以了,查询的时候只需要判断最靠前的一个位置的值是否小于
复杂度是
考虑优化一下,因为最多就只会把这个块全选完,因此变化量是
复杂度
不过因为常数比较大,实测设块长为
不过本人实现不好,必须开 O2 才能通过。
const int BL = 220;
int tr[BL][N];
int dec[N];
int bel[N], L[N], R[N];
int B;
LL tot[N];
int Min[N * 4], tag[N * 4], Pos[N * 4];
void pushup(int k) {
Min[k] = Min[k << 1 | 1];
Pos[k] = Pos[k << 1 | 1];
if (Min[k << 1] < Min[k << 1 | 1])
Min[k] = Min[k << 1], Pos[k] = Pos[k << 1];
}
void Build(int k, int l, int r) {
tag[k] = 0;
if (l == r) {
Min[k] = dec[l];
Pos[k] = l;
return;
}
int mid = (l + r) >> 1;
Build(k << 1, l, mid);
Build(k << 1 | 1, mid + 1, r);
pushup(k);
}
void Push(int k, int v) {
tag[k] += v;
Min[k] += v;
}
void pushdown(int k) {
if (tag[k]) {
Push(k << 1, tag[k]);
Push(k << 1 | 1, tag[k]);
tag[k] = 0;
}
}
void Addate(int k, int l, int r, int L, int R) {
if (L <= l && r <= R) {
Push(k, 1);
return;
}
pushdown(k);
int mid = (l + r) >> 1;
if (L <= mid)
Addate(k << 1, l, mid, L, R);
if (R > mid)
Addate(k << 1 | 1, mid + 1, r, L, R);
pushup(k);
}
void Ban(int k, int l, int r, int x) {
if (l == r) {
Min[k] = 1e9;
return;
}
pushdown(k);
int mid = (l + r) >> 1;
if (x <= mid)
Ban(k << 1, l, mid, x);
else
Ban(k << 1 | 1, mid + 1, r, x);
pushup(k);
}
void rebuild(int x) {
Build(1, L[x], R[x]);
int len = 0;
for (int i = 1; i <= n + 1; i++) {
int o = Min[1];
if (o >= 1e9) {
for (; i <= n + 1; i++) tr[x][i] = i - len;
break;
}
for (int j = i; j <= o; j++) tr[x][j] = j - len;
len++;
tr[x][o + 1] = o + 1 - len;
int p = Pos[1];
Ban(1, L[x], R[x], p);
Addate(1, L[x], R[x], L[x], p);
i = o + 1;
}
}
int pos(LL x) {
int l = 0, r = n, mid, ans;
while (l <= r) {
mid = (l + r) >> 1;
if (tot[mid] < x)
ans = mid, l = mid + 1;
else
r = mid - 1;
}
return ans + 1;
}
inline int reduce(int x, int l, int r) {
for (int i = r - 1; i >= l; i--) x -= (dec[i] < x);
return x;
}
int LittleFools(int i, int x) { return reduce(x, L[bel[i]], i); }
LL LittleFind(int i, int x, int y) {
for (i--; x != y && i >= 1; i--) {
x -= (dec[i] < x);
y -= (dec[i] < y);
}
return tot[i] + x;
}
inline LL LCA(int i, int x, int j, int y) {
while (abs(bel[i] - bel[j]) > 1) {
if (bel[i] < bel[j])
swap(i, j), swap(x, y);
if (i != L[bel[i]])
x = LittleFools(i, x), i = L[bel[i]];
i = L[bel[i] - 1], x = tr[bel[i]][x];
}
if (i < j)
swap(i, j), swap(x, y);
x = reduce(x, j, i);
i = j;
int fi = LittleFools(i, x), fj = LittleFools(j, y);
bool fl = 0;
while (fi != fj) {
if (!fl)
i = L[bel[i]], j = i;
else
i = L[bel[i] - 1], j = i;
x = fi;
y = fj;
fl = 1;
fi = tr[bel[i] - 1][x];
fj = tr[bel[j] - 1][y];
}
return LittleFind(i, x, y);
}
void solve() {
B = 1700;
for (int i = 1; i <= n; i++) {
dec[i] = a[i] - tot[i - 1];
tot[i] = tot[i - 1] + i;
}
for (int i = 1; i <= n; i++) {
bel[i] = (i - 1) / B + 1;
if (!L[bel[i]])
L[bel[i]] = i;
R[bel[i]] = i;
}
int cnt = bel[n];
bel[n + 1] = cnt + 1;
L[bel[n + 1]] = n + 1;
for (int i = 1; i <= cnt; i++) rebuild(i);
LL sum = 1ll * (n + 1) * (n + 2) / 2;
LL ans = 0;
while (m--) {
LL x, y;
read(x);
read(y);
x = (x - 1 + 1ll * tp * ans) % sum + 1;
y = (y - 1 + 1ll * tp * ans) % sum + 1;
int i = pos(x), u = x - tot[i - 1];
int j = pos(y), v = y - tot[j - 1];
ans = LCA(i, u, j, v);
printf("%lld\n", ans);
}
}