ZR956 集合 (01 Trie)

Description

在这里插入图片描述

Solution

review 一下异或的 trick
a ⊕ b ⊕ c = a ⊕ ( b ⊕ c ) a \oplus b \oplus c =a \oplus(b \oplus c) abc=a(bc)

a ⊕ b ⊕ b = a a \oplus b \oplus b = a abb=a

对于没有加操作的时候,可以维护一个异或的懒标记,当有异或操作的时候懒标记 t a g ⊕ x tag \oplus x tagx,插入删除时,对应将 x ⊕ t a g x \oplus tag xtag 插入或删除,再最后输出的时候将每一项 ⊕ t a g \oplus tag tag

考虑加法操作。由于只加一,可以从二进制入手。找规律发现一个数 k k k 的二进制位数为 ⌊ l o g 2 ( k ) ⌋ + 1 \lfloor log_2(k) \rfloor + 1 log2(k)+1,我们令一个数的最低位到最高位依次为 a 1 ∼ a 30 a_1 \sim a_{30} a1a30,那么 + 1   m o d   2 30 +1 \bmod 2^{30} +1mod230 为找到一个满足 a i = 0 a_i = 0 ai=0 a 1 ∼ a i − 1 = 1 a _ 1 \sim a_ {i - 1} = 1 a1ai1=1,然后把 1 1 1 变成 0 0 0 0 0 0 变成 1 1 1,如果不存在满足条件的 i i i,那么认为 i = 31 i = 31 i=31,那么需要把所有的 a i a_i ai 都反转成 0 0 0

按照插入数字从低到高维护一棵字典树,用 v a l x val_x valx 表示字典树节点 x x x 对应值出现的次数为 v a l x val_x valx,那么插入和删除操作就是对应的 + 1 +1 +1 − 1 -1 1 + 1 +1 +1 就是 + t a g +tag +tag, 输入时就直接插入。当找到了满足条件的 i i i 时,反转相当于交换 i i i 的左右儿子。

get 了 trie 的神奇用法。

Code

#include <bits/stdc++.h>
using namespace std;
#define re register
#define F first
#define S second
typedef long long ll;
typedef pair<int, int> P;
const int N = 6e6 + 5;
const int INF = 0x3f3f3f3f;
int read() {
	int x = 0, f = 0; char ch = 0;
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
int n, q, tag, ch[N][2], val[N], ans[N], cnt, tot = 1;
void insert(int k){
	int p = 1;
	for (int i = 0; i < 30; i++, k >>= 1){
		int c = k & 1;
		if(!ch[p][c]) ch[p][c] = ++tot;
		p = ch[p][c];
	}
	val[p]++;
}
void erase(int k){
	int p = 1;
	for (int i = 0; i < 30; i++, k >>= 1) p = ch[p][k & 1];
	val[p]--;
}
void add(int k){
	int p = 1;
	for (int i = 0; i < 30; i++, k >>= 1){
		swap(ch[p][0], ch[p][1]);
		p = ch[p][k & 1];
	}
}
void dfs(int x, int dep, int p){
	if(dep == 30){
		for (int i = 1; i <= val[x]; i++) ans[++cnt] = p ^ tag;
		return ;
	}
	if (ch[x][0]) dfs(ch[x][0], dep + 1, p);
	if (ch[x][1]) dfs(ch[x][1], dep + 1, p | (1 << dep));
}
int main(){
	n = read(), q = read();
	for (int i = 1; i <= n; i++){
		int x = read(); insert(x);
	}
	while (q--){
		int opt = read(), x;
		if(opt != 3) x = read();
		if(opt == 1) insert(x ^ tag);
		if(opt == 2) erase(x ^ tag);
		if(opt == 3) add(tag);
		if(opt == 4) tag ^= x;
	}
	dfs(1, 0, 0);
	sort(ans + 1, ans + cnt + 1);
	for (int i = 1; i <= cnt; i++) printf("%d ", ans[i]);
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值