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

本题为例。
$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

浙公网安备 33010602011771号