小trick

对于一系列对每个数进行 \(XOR,AND,OR\) 方法的处理:

QQ_1752918676250

本题为例。

$XOR $是简单的,在 Trie 上 pushdown 时交换左右儿子即可。

查询也是简单的,直接贪心走就行。

对于 \(AND,OR\) 本质上是合并 Trie 子树。

若想完成标记复合,则必须记录一个 \(4\) 的映射,表示 \(0 \to 0/1, 1 \to 0/1\) 共四种情况,对这个映射是可以复合的,标记下传可能需要 \(\log\) 的时间代价,此时对于需要合并子树的暴力合并即可,毛咕咕分析是 \(\log^2\) 的。

#include <bits/stdc++.h>
using namespace std;
#define QwQ01AwA return 0
#define uint unsigned int
#define i64 long long
#define u64 unsigned long long
#define dbg(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
#define file(x) freopen(x ".in", "r", stdin), freopen(x ".out", "w", stdout)
#define look_time cerr << 1.0 * clock() / CLOCKS_PER_SEC << '\n'
template <typename T> void ckmax(T &x, const T y) {x = max(x, y);}
template <typename T> void ckmin(T &x, const T y) {x = min(x, y);}

const int limV = 31;
const int N = 2e5 + 5;
u64 w[4][4];
struct Tree {
	int ls, rs;
	u64 tag;
} t[N * 35];
int bit(u64 x, int i) {return (x >> i) & 1;}
#define ls(p) (t[p].ls)
#define rs(p) (t[p].rs)
int cnt, rt;
void down(int p, int d) ;
int merge(int u, int v, int d) ;
int merge(int u, int v, int d) {
	if (!u || !v) return u ^ v;
	if (d == -1) return u;
	down(u, d), down(v, d);
	ls(u) = merge(ls(u), ls(v), d - 1);
	rs(u) = merge(rs(u), rs(v), d - 1);
	return u;
}
void down(int p, int d) {
	if (!t[p].tag) return ;
	int s = bit(t[p].tag, 2 * d) | (bit(t[p].tag, 2 * d + 1) << 1);
	if (s == 1) ls(p) = merge(ls(p), rs(p), d - 1), rs(p) = 0;
	else if (s == 2) rs(p) = merge(rs(p), ls(p), d - 1), ls(p) = 0;
	else if (s == 3) swap(ls(p), rs(p));
	u64 hls = 0, hrs = 0;
	for (int i = 0; i < d; i++) {
		s = t[p].tag & 3;
		hls |= (w[t[ls(p)].tag & 3][s] << (2 * i));
		hrs |= (w[t[rs(p)].tag & 3][s] << (2 * i));
		t[p].tag >>= 2, t[ls(p)].tag >>= 2, t[rs(p)].tag >>= 2;
	}
	t[ls(p)].tag = hls, t[rs(p)].tag = hrs, t[p].tag = 0;
}
void ins(int &p, uint x, int d) {
	if (!p) p = ++cnt;
	if (d == -1) return ;
	down(p, d);
	if (!bit(x, d)) ins(ls(p), x, d - 1);
	else ins(rs(p), x, d - 1);
}
i64 query(int p, uint x, int d) {
	if (d == -1) return 0;
	down(p, d);
	if (bit(x, d)) {
		if (ls(p)) return query(ls(p), x, d - 1) + (1ll << d);
		else return query(rs(p), x, d - 1);
	} else {
		if (rs(p)) return query(rs(p), x, d - 1) + (1ll << d);
		else return query(ls(p), x, d - 1);	
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	w[0][0] = 0, w[0][1] = 1, w[0][2] = 2, w[0][3] = 3;
	w[1][0] = 1, w[1][1] = 1, w[1][2] = 2, w[1][3] = 2;
	w[2][0] = 2, w[2][1] = 1, w[2][2] = 2, w[2][3] = 1;
	w[3][0] = 3, w[3][1] = 1, w[3][2] = 2, w[3][3] = 0;
	int n, m;
	cin >> n >> m;
	uint x;
	for (int i = 1; i <= n; i++) cin >> x, ins(rt, x, limV);
	while (m--) {
		int op;
		cin >> op >> x;
		if (op == 1) ins(rt, x, limV);
		else if (op == 5) cout << query(rt, x, limV) << '\n';
		else {
			u64 val = 0;
			for (int i = 0; i <= limV; i++) {
				if (op == 2) {
					if (bit(x, i)) val |= (w[t[rt].tag & 3][2] << (2 * i));
					else val |= ((t[rt].tag & 3) << (2 * i));
				} else if (op == 3) {
					if (!bit(x, i)) val |= (w[t[rt].tag & 3][1] << (2 * i));
					else val |= ((t[rt].tag & 3) << (2 * i));
				} else {
					if (bit(x, i)) val |= (w[t[rt].tag & 3][3] << (2 * i));
					else val |= ((t[rt].tag & 3) << (2 * i));
				}
				t[rt].tag >>= 2;
			}
			t[rt].tag = val;
		}
	}
	QwQ01AwA;
}

另一种方法是类似于 CF1515H 的处理,每个子树记录下只有一个儿子的深度集合,对于 \(AND,OR\) 若修改只影响这些深度,则可以直接打标记走人,此时标记只有交换左右儿子,可以复合,复杂度是 \(\log\) 的。 submission

posted @ 2025-07-19 18:01  Anonymely  阅读(36)  评论(0)    收藏  举报