题解 CF706D 【Vasiliy's Multiset】
算法分析:Trie 树
题目中唯一的运算是按位异或,我们可以考虑按位异或的本质。既然是一位一位异或,那么我们直接把 \(x\) 转化成一个二进制数不就好了吗?
在插入时,我们可以先将 \(x\) 转化为一个32位二进制数,然后从高位到低位插入 Trie 树,并对每个树上每个节点 \(p\) 记录一个 \(rec_p\) 表示这个节点被 \(rec_p\) 个数字使用过。
在查找时,优先沿着相反的边向下移动指针,并累加答案即可。
在删除时,对沿途节点的 \(rec_p\) 减1即可。
注意
-
数字必须从高位到低位插入 Trie 树,否则会引起冲突。
-
与一般 Trie 树不同的地方是,本题中不需要打结尾标记(显然每个数都是32位),但是需要额外记录每一个节点被几个数使用。
-
数组记得开大一点,否则容易 RE。
code:
#include<bits/stdc++.h>
#define reg register
#define F(i,a,b) for(reg int i=(a);i<=(b);i++)
using namespace std;
inline char get_char();
const int N=3e6+10;
int T,n,cnt=1;
int c[40],trie[N][3];
int rec[N];
inline void change(long long x) {
memset(c,0,sizeof(c));
int ct=0;
while(x)c[++ct]=x&1,x>>=1;
}
inline int reply(){
int p=1,s=0;
for(reg int i=33;i>=1;i--){
int &k=c[i];
s<<=1;
if(!trie[p][k^1] or !rec[trie[p][k^1]])p=trie[p][k];
//相反的边走不通
else p=trie[p][k^1],s|=1;//走相反的边
}
return s;
}
inline void insert(){//插入
int p=1;
for(reg int i=33;i>=1;i--){
int &k=c[i];
if(!trie[p][k])trie[p][k]=++cnt;
p=trie[p][k],rec[p]++;//记录该节点被几个数用过
}
}
inline void _delete(){//删除
int p=1;
for(reg int i=33;i>=1;i--){
int &k=c[i];
p=trie[p][k];
rec[p]--;//减去这个数的贡献
}
}
int main() {
scanf("%d",&n);
insert();//插入开始时的0
while(n--){
static char c;
static int x;
c=get_char();
scanf("%d",&x);
change(x);//将x变成二进制数
if(c=='?')printf("%d\n",reply());
else if(c=='+')insert();
else _delete();
}
return 0;
}
inline char get_char(){//读入字符
reg char c=getchar();
while(c!='+' and c!='-' and c!='?')c=getchar();
return c;
}
欢迎交流讨论,请点个赞哦~
本文来自博客园,作者:Maplisky,转载请注明原文链接:https://www.cnblogs.com/lbh2021/p/14920569.html