\(\text{Solution}\)
Trie
的高阶操作
用权值线段树结构表示这棵 Trie
,那么操作时直接线段树分裂取出值域在 \([l,r]\) 内的数构成的 Trie
,也即线段树
接着在这棵线段树上修改,修改完后合并回去
看三个位运算,发现 x&y=((x^U)|(y^U))^U}
于是变成两个运算,xor
很好维护,直接打标记看是否需要交换左右儿子即可
or
并不能很好用标记处理,但考虑到数的种类数的变化,or
是很能减少种类数的
某一位或上 \(1\) 可以把左儿子并到右儿子上,这样线段树结点数就减少了
可以暴力跑整棵树,合并减少结点数,均摊复杂度就是 \(O(n\log^2V)\)
但注意到有些情况不能减少结点数,这样复杂度就错了
or
后没变化,或者 or
后结果等价于 xor
结果,这两种情况可以通过打异或标记处理
为了判断当前子树 or
做下取能否减少结点数,就要维护线段树每个结点所辖子树的每一层是否具有左右儿子且其到根路径上经过的结点是左儿子还是右儿子
是左儿子还是右儿子正确的理解是这棵线段树代表的 Trie
树边值为 \(0\) 还是 \(1\)
\(\text{Code}\)
#include <bits/stdc++.h>
#define IN inline
using namespace std;
template <typename Tp>
IN void read(Tp &x) {
x = 0; char ch = getchar(); int f = 0;
for(; !isdigit(ch); f = (ch == '-' ? 1 : f), ch = getchar());
for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
if (f) x = ~x + 1;
}
const int N = 2e5 + 5, len = (1 << 20);
int rt, size, ls[N * 41], rs[N * 41], sum[N * 41], tg[N * 41], tgl[N * 41], tgr[N * 41];
IN void pushup(int p) {
sum[p] = sum[ls[p]] + sum[rs[p]],
tgl[p] = tgl[ls[p]] | tgl[rs[p]],
tgr[p] = tgr[ls[p]] | tgr[rs[p]];
}
IN void pushXor(int p, int v, int d) {
if (v >> d & 1) swap(ls[p], rs[p]);
int L = tgl[p], R = tgr[p]; tg[p] ^= v;
tgl[p] = (L & (v ^ len - 1)) | (R & v);
tgr[p] = (L & v) | (R & (v ^ len - 1));
}
IN void pushdown(int p, int d) {
if (!tg[p]) return;
pushXor(ls[p], tg[p], d - 1), pushXor(rs[p], tg[p], d - 1), tg[p] = 0;
}
void insert(int &p, int x, int d) {
if (!p) p = ++size;
if (d == -1) return tgl[p] = x ^ len - 1, tgr[p] = x, sum[p] = 1, void();
if (x >> d & 1) insert(rs[p], x, d - 1); else insert(ls[p], x, d - 1);
pushup(p);
}
void split(int &x, int &y, int l, int r, int ql, int qr, int d) {
if (!x) return;
if (ql <= l && r <= qr) return y = x, x = 0, void();
int mid = l + r >> 1; y = ++size;
pushdown(x, d);
if (ql <= mid) split(ls[x], ls[y], l, mid, ql, qr, d - 1);
if (qr > mid) split(rs[x], rs[y], mid + 1, r, ql, qr, d - 1);
pushup(x), pushup(y);
}
void merge(int &x, int y, int d) {
if (!x || !y) return x |= y, void();
if (d == -1) return;
pushdown(x, d), pushdown(y, d), merge(ls[x], ls[y], d - 1), merge(rs[x], rs[y], d - 1), pushup(x);
}
IN void pushOr(int p, int v, int d) {
if (d == -1 || !p) return;
if (!(v & tgl[p] & tgr[p])) return pushXor(p, v & tgl[p], d), void();
pushdown(p, d);
if (v >> d & 1) pushXor(ls[p], 1 << d, d - 1), merge(rs[p], ls[p], d - 1), ls[p] = 0;
pushOr(ls[p], v, d - 1), pushOr(rs[p], v, d - 1), pushup(p);
}
int main() {
int n, q; read(n), read(q);
for(int i = 1, x; i <= n; i++) read(x), insert(rt, x, 19);
for(int op, x, y, z, now; q; --q) {
read(op), read(x), read(y), now = 0, split(rt, now, 0, len - 1, x, y, 19);
if (op == 4) printf("%d\n", sum[now]);
else {
read(z);
if (op == 1) pushXor(now, len - 1, 19), pushOr(now, z ^ len - 1, 19), pushXor(now, len - 1, 19);
if (op == 2) pushOr(now, z, 19);
if (op == 3) pushXor(now, z, 19);
}
merge(rt, now, 19);
}
}