Luogu 4883 mzf的考验

题意:

给定长度为 \(n\) 的序列 \(a_i\)\(m\) 次操作,操作分为 \(3\) 种:

  1. 给定两个整数 \(l,r\),翻转区间 \([l,r]\)。即 \(a_l,a_{l+1},...,a_r\to a_r,a_{r-1},...,a_l\)
  2. 给定三个整数 \(l,r,d\),对于 \(i\in [l,r]\)\(a_i\gets a_i\oplus d\)\(\oplus\) 表示异或运算。
  3. 给定两个整数 \(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;
}
posted @ 2023-07-21 08:24  Ender_32k  阅读(15)  评论(0编辑  收藏  举报