题解 CF896E Welcome home, Chtholly
update on 2021.11.12 修正了一些错误。
题意
给定一个长为 $n$ 的序列 $a$,$m$ 次操作:
- 将 $a_{l,\cdots,r}$ 中大于 $x$ 的数减去 $x$。
- 查询区间 $a_{l,\cdots,r}$ 中等于 $x$ 的数的个数。
数据范围:$1\le n,m\le10^5,0\le a_i,x\le10^5$
题解
设 $w$ 为值域。
对于每个块内,记录每一个值出现的次数,维护一个最大值 $maxn$ 和一个区间减的标记。
对于修改操作,散块直接暴力重构。
若修改一个整块,分类讨论一下,设更新所有值为 $v$ 的数时间复杂度为 $O(\text{ds})$:
-
若 $maxn\le 2x$,把块内所有大于 $x$ 的数减去 $x$。此时需更新的区间为 $\left[x+1,maxn\right]$,此时用 $O(maxn-x)\times O(\text{ds})$ 的代价使 $maxn$ 减少了 $maxn-x$。
-
若 $maxn>2x$,把块内所有小于等于 $x$ 的数加上 $x$,再打上区间减 $x$ 的标记,需更新的区间为 $\left[1,x\right]$,此时用 $O(x)\times O(\text{ds})$ 的代价使 $maxn$ 的代价减少了 $x$。
首先所有的值会越来越小,又 $maxn$ 比 $2x$ 大就会打标记,这就保证了 $maxn$ 不会溢出,每个值每个块最多会修改 $w$ 次,复杂度即为 $O(w)$。
散块查询暴力统计,整块查询返回块内值 $x$ 记录的个数即可,时间复杂度为 $O(\sqrt{n})$。
空间复杂度为即为 $O(w\sqrt{n})$,$w$ 为值域。
考虑怎么更新每个值对应的一堆数。
比较好写的写法是并查集,对于任意一个其值为 $v$ 的并查集,维护一个 $rt$,表示序列中第一个值为 $v$ 的数的下标,对于等于同一个值 $x$ 的数,则全并到 $fa_{rt_x}$ 上。
在合并的时候,设把值为 $x$ 的并查集合并到值为 $y$ 的并查集上。
- 若 $rt_y$ 不存在,则把 $rt_x$ 全放到 $rt_y$ 上,并使 $rt_y$ 指向 $y$。
- 若 $rt_x$ 存在,则直接合并 $rt_x$ 和 $rt_y$ 两个并查集。
用这种并查集可把维护每个块的复杂度优化到了 $O(\alpha(n))\approx O(1)$,总体时间复杂度是 $O((n+m)\sqrt{n})$,空间复杂度为 $O(w\sqrt{n})$。
已经足够通过本题,然而还可以优化。
有个逐块处理的 trick,即离线下来一块一块处理,可把空间复杂度优化到 $O(w)$。
也许可以加启发式合并?但我不会写,而且重构次数较多,启发式合并优化意义不大。
#include <bits/stdc++.h>
using namespace std;
namespace IO {
#if ONLINE_JUDGE
#define getc() (IS == IT && (IT = (IS = ibuf) + fread(ibuf, 1, IL, stdin), IS == IT) ? EOF : *IS++)
#else
#define getc() getchar()
#endif
const int IL = 1 << 20, OL = 1 << 21;
int olen = 0;
char ibuf[IL], *IS = ibuf, *IT = ibuf, obuf[OL];
inline int read() {
register char ch = getc(); register int f = 1, x = 0;
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getc(); }
while(isdigit(ch)) x = x * 10 + ch - 48, ch = getc();
return x * f;
}
inline void flush() { fwrite(obuf, 1, olen, stdout); olen = 0; }
inline void putc(register char ch) { obuf[olen++] = ch; }
template<class T>
inline void write(register T x) {
if(x < 0) obuf[olen++] = '-', x = -x;
if(x > 9) write(x / 10);
obuf[olen++] = x % 10 + 48;
}
} using namespace IO;
const int N = 1e6 + 10, M = 5e5 + 10, W = 1e5 + 10;
int n, m;
int a[N], val[N], s, t, fa[N], siz[N], rt[W], mx, tag, L;
struct ask {
int opt, l, r, x, ans;
}q[M];
inline int find(int x) {
if(fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
inline void init() { L = sqrt(n); }
inline void build() {
mx = tag = 0;
for(register int i = s; i <= t; i++) {
mx = max(mx, a[i]);
if(!rt[a[i]])
fa[i] = rt[a[i]] = i, val[i] = a[i];
else
fa[i] = rt[a[i]];
siz[a[i]]++;
}
}
inline void merge(int x, int y) {
if(rt[y]) fa[rt[x]] = rt[y];
else {
rt[y] = rt[x];
val[rt[y]] = y;
}
siz[y] += siz[x];
rt[x] = siz[x] = 0;
}
inline void total_update(int x) {
if(mx - tag > (x << 1)) {
for(register int i = tag + 1; i <= tag + x; i++)
if(rt[i])
merge(i, i + x);
tag += x;
}
else {
for(register int i = mx; i > tag + x; i--)
if(rt[i])
merge(i, i - x);
mx = min(mx, tag + x);
}
}
inline void part_update(int l, int r, int x) {
for(register int i = s; i <= t; i++) {
a[i] = val[find(i)];
rt[a[i]] = siz[a[i]] = 0;
a[i] -= tag;
}
int nl = max(s, l), nr = min(t, r);
for(register int i = s; i <= t; i++)
val[i] = fa[i] = 0;
for(int i = nl; i <= nr; i++)
a[i] -= x * (a[i] > x);
build();
}
inline void query(int num) {
if(q[num].l <= s && t <= q[num].r)
q[num].ans += siz[q[num].x + tag];
else {
int nl = max(s, q[num].l), nr = min(t, q[num].r);
for(register int i = nl; i <= nr; i++)
if(val[find(i)] - tag == q[num].x)
q[num].ans++;
}
}
int main() {
n = read(), m = read();
for(register int i = 1; i <= n; i++)
a[i] = read();
for(register int i = 1; i <= m; i++) {
q[i].opt = read(), q[i].l = read(), q[i].r = read(), q[i].x = read();
}
L = sqrt(n);
for(register int now = 1; now <= n; now += L) {
memset(siz, 0, sizeof(siz));
memset(rt, 0, sizeof(rt));
s = now, t = min(n, now + L - 1);
build();
for(register int i = 1; i <= m; i++) {
if(t < q[i].l || s > q[i].r)
continue;
if(q[i].opt == 1) {
if(q[i].l <= s && q[i].r >= t)
total_update(q[i].x);
else
part_update(q[i].l, q[i].r, q[i].x);
}
else if(tag + q[i].x <= M)
query(i);
}
}
for(register int i = 1; i <= m; i++)
if(q[i].opt == 2)
write(q[i].ans), putc('\n');
flush();
return 0;
}

浙公网安备 33010602011771号