P8969 分块

· · 题解

以下用 \mathrm{popc} 代替 \mathrm{popcount}

先考虑全局加全局 \mathrm{popc} 怎么做。

维护一个 tag 表示目前全局加了多少,于是加法变成了 O(1) 的。

查询取决于你用什么东西去维护这个映射,可以做到 $O(1),O(\log\log V), O(\log V)$ ,这不重要,不作展开。 然后你发现这个东西就可以搬到一些数据结构上维护了,当然线段树上怎么维护我也不会,所以我要写分块。 整块的维护我们按上面的做法维护就好了,关键是散块,对散块的加操作会破坏掉整个映射(可能有 $a_i=a_j$,但是你需要对 $a_j$ 加上一个数),维护映射的 $\mathrm{popc}$ 操作会破坏掉加法的 `tag`。 于是你要对这个散块把该传的标记都传下去,然后维护一个新的映射,这个过程是 $O(B+\log V)$ 的,$B$ 是块长。 然后就可以分析复杂度了。 加法是 $O(q(B+\log V+\frac{n}{B}))$。 对于 $\mathrm{popc}$ 考虑分析势能,变颜色的复杂度是 $O(B+\log V)$(第一次 $\mathrm{popc}$ 或者散块重构),然后最多变 $O(q)$ 次,所以是 $O(q(B+\log V))$,直接 $\mathrm{popc}$ 的复杂度是 $O(q\frac{n}{B}\log V)$。 查询不是瓶颈,反正不高于 $O(q\log V)$。 同时还解决了区间求和。 理论上 $B=\sqrt{n\log V}$ 是最优的,此时复杂度 $O(q\sqrt{n\log V})$,当然性能瓶颈在于重构,所以小一点总是好的。 ```cpp #include <bits/stdc++.h> #define fi first #define se second using namespace std; using ll = long long; using pll = pair<ll, ll>; const int N = 3e5 + 5, S = 389, B = 777, LG = 51; int n, m, bel[N], st[B], ed[B], siz[B], tmp[LG]; bool pope[B]; ll a[N], tag[B], buk[LG]; pll ys[B][LG]; inline void rebuild(int bid) { if (!pope[bid]) { for (int i = st[bid]; i <= ed[bid]; i++) a[i] += tag[bid]; } else { for (int i = 1; i <= siz[bid]; i++) buk[ys[bid][i].fi] = ys[bid][i].se + tag[bid]; for (int i = st[bid]; i <= ed[bid]; i++) a[i] = buk[a[i]]; } pope[bid] = false; tag[bid] = 0; } inline void mod_add(int l, int r, ll k) { int p = bel[l], q = bel[r]; if (p == q) { rebuild(p); for (int i = l; i <= r; i++) a[i] += k; } else { rebuild(p); for (int i = l; i <= ed[p]; i++) a[i] += k; for (int i = p + 1; i < q; i++) tag[i] += k; rebuild(q); for (int i = st[q]; i <= r; i++) a[i] += k; } } inline void mod_pop(int l, int r) { int p = bel[l], q = bel[r]; if (p == q) { rebuild(p); for (int i = l; i <= r; i++) a[i] = __builtin_popcountll(a[i]); } else { rebuild(p); for (int i = l; i <= ed[p]; i++) a[i] = __builtin_popcountll(a[i]); for (int i = p + 1; i < q; i++) { if (!pope[i]) { memset(tmp, -1, sizeof tmp), siz[i] = 0; for (int j = st[i]; j <= ed[i]; j++) { a[j] = __builtin_popcountll(a[j] + tag[i]); tmp[a[j]] = a[j]; } for (int j = 0; j < LG; j++) if (~tmp[j]) ys[i][++siz[i]] = pll(tmp[j], tmp[j]); } else { for (int j = 1; j <= siz[i]; j++) ys[i][j].se = __builtin_popcountll(ys[i][j].se + tag[i]); } pope[i] = true; tag[i] = 0; } rebuild(q); for (int i = st[q]; i <= r; i++) a[i] = __builtin_popcountll(a[i]); } } inline ll query(int x) { int p = bel[x]; if (!pope[p]) return a[x] + tag[p]; return lower_bound(ys[p] + 1, ys[p] + siz[p] + 1, pll(a[x], 0))->se + tag[p]; } signed main() { cin.tie(nullptr)->sync_with_stdio(false); // freopen("test.in", "r", stdin); // freopen("dsjaikjf.out", "w", stdout); cin >> n >> m; // if (n > 2000) { // return 0; // } // ofstream fout("fdsa.out"); for (int i = 1; i <= n; i++) cin >> a[i]; const int sq = sqrt(n / 2); for (int i = 1; i <= n; i++) bel[i] = (i - 1) / sq + 1; for (int i = 1; i <= bel[n]; i++) st[i] = ed[i - 1] + 1, ed[i] = st[i] + sq - 1; ed[bel[n]] = n; while (m--) { char op; int l, r; ll k; cin >> op; if (op == 'A') { cin >> l >> r >> k; mod_add(l, r, k); } else if (op == 'P') { cin >> l >> r; mod_pop(l, r); } else { cin >> l; cout << query(l) << '\n'; } // fout << op << ": "; // for (int i = 1; i <= n; i++) // fout << query(i) << ' '; // fout << endl; } return 0; } ```