Luogu 4883 mzf的考验
题意:
给定长度为 \(n\) 的序列 \(a_i\),\(m\) 次操作,操作分为 \(3\) 种:
- 给定两个整数 \(l,r\),翻转区间 \([l,r]\)。即 \(a_l,a_{l+1},...,a_r\to a_r,a_{r-1},...,a_l\)。
- 给定三个整数 \(l,r,d\),对于 \(i\in [l,r]\),\(a_i\gets a_i\oplus d\)。\(\oplus\) 表示异或运算。
- 给定两个整数 \(l,r\),查询 \(\sum\limits_{i\in [l,r]}a_i\) 的值。
平衡树卡常题。
每一二进制位互相独立,所以可以建 \(20\) 棵平衡树,每棵维护一个二进制位。操作变成 \(01\) 序列支持区间翻转,区间取反以及查询区间 \(1\) 的个数。
平衡树维护翻转 \(\text{rev}\) 标记、反转 \(\text{tag}\) 标记和区间 \(1\) 的个数 \(\text{cnt}\) 即可。答案就是 \(\sum\limits_{i=0}^{19}2^i\times\text{cnt}\)。
然后你直接这么写发现你常数炸飞了,随手造一组数据跑了 16s。因为有很多信息比如 \(\text{siz,rd}\) 甚至左儿子右儿子对于每棵平衡树都是相同的,然而你求了 \(20\) 遍。
发现可以直接开一棵平衡树,\(\text{tag}\) 直接维护这个子树应该异或的值 \(d\),而不再是 \(d\) 的某一位。\(\text{cnt}\) 还是要维护每一位,多记录一个 \(\text{sum}\) 表示子树和,每次 pushup 和 pushdown 的时候根据每一位的 \(\text{cnt}\) 可以推出来。
做完了,\(O(n\log n\log w)\)。
mt19937 rnd(time(0));
const int maxn = 1e5 + 100;
int n, m, a[maxn];
int rt, tot, x, y, z;
ll sum[maxn];
int rd[maxn], sz[maxn], cnt[maxn][25], rev[maxn], tag[maxn], val[maxn], ch[maxn][2];
void pushup(int x) {
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1, sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x];
for (int i = 19; ~i; i--) cnt[x][i] = cnt[ch[x][0]][i] + cnt[ch[x][1]][i] + ((val[x] >> i) & 1);
}
void pushrev(int x) {
if (!rev[x]) return;
swap(ch[x][0], ch[x][1]);
if (ch[x][0]) rev[ch[x][0]] ^= 1;
if (ch[x][1]) rev[ch[x][1]] ^= 1;
rev[x] = 0;
}
void pushxor(int x) {
if (!tag[x]) return;
if (ch[x][0]) {
val[ch[x][0]] ^= tag[x], tag[ch[x][0]] ^= tag[x], sum[ch[x][0]] = 0;
for (int i = 19; ~i; i--) {
if ((tag[x] >> i) & 1) cnt[ch[x][0]][i] = sz[ch[x][0]] - cnt[ch[x][0]][i];
sum[ch[x][0]] += (1ll << i) * cnt[ch[x][0]][i];
}
}
if (ch[x][1]) {
val[ch[x][1]] ^= tag[x], tag[ch[x][1]] ^= tag[x], sum[ch[x][1]] = 0;
for (int i = 19; ~i; i--) {
if ((tag[x] >> i) & 1) cnt[ch[x][1]][i] = sz[ch[x][1]] - cnt[ch[x][1]][i];
sum[ch[x][1]] += (1ll << i) * cnt[ch[x][1]][i];
}
}
tag[x] = 0;
}
void pushdown(int x) { pushrev(x), pushxor(x); }
void split(int p, int k, int &x, int &y) {
if (!p) return x = y = 0, void();
pushdown(p);
if (sz[ch[p][0]] < k) x = p, split(ch[x][1], k - sz[ch[x][0]] - 1, ch[x][1], y);
else y = p, split(ch[y][0], k, x, ch[y][0]);
pushup(p);
}
int merge(int x, int y) {
if (!x || !y) return x | y;
pushdown(x), pushdown(y);
if (rd[x] < rd[y]) return ch[x][1] = merge(ch[x][1], y), pushup(x), x;
else return ch[y][0] = merge(x, ch[y][0]), pushup(y), y;
}
void reverse(int l, int r) {
split(rt, l - 1, x, y), split(y, r - l + 1, y, z);
rev[y] ^= 1, rt = merge(x, merge(y, z));
}
void update(int l, int r, int d) {
split(rt, l - 1, x, y), split(y, r - l + 1, y, z);
val[y] ^= d, tag[y] ^= d, sum[y] = 0;
for (int i = 19; ~i; i--) {
if ((d >> i) & 1) cnt[y][i] = sz[y] - cnt[y][i];
sum[y] += (1ll << i) * cnt[y][i];
}
rt = merge(x, merge(y, z));
}
ll query(int l, int r) {
split(rt, l - 1, x, y), split(y, r - l + 1, y, z);
ll res = sum[y]; rt = merge(x, merge(y, z));
return res;
}
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) {
a[i] = read();
sum[i] = val[i] = a[i], rd[i] = rnd(), sz[i] = 1;
for (int j = 19; ~j; j--)
if ((a[i] >> j) & 1) cnt[i][j] = 1;
rt = merge(rt, i);
}
while (m--) {
int op = read(), l = read(), r = read();
if (op == 1) reverse(l, r);
else if (op == 2) update(l, r, read());
else write(query(l, r)), puts("");
}
return 0;
}