题目
q<=2e5个操作
操作有三种
+ x 代表把x加入multiset A
- x 代表把x从multiset A里删去一个
? x 询问multiset里的一个值y,使得ans=y^x最大,输出ans
注意0恒在multiset中,无需加入
思路来源
https://www.cnblogs.com/dwtfukgv/p/5771127.html
航神
题解
这个东西,可能是叫可持久化字典树,毕竟支持删除
然而也很简单啦,如果(x>>i)&1这一位能取它的异或值就取它的异或值
如果能异或,这一位的答案肯定是有1的,
否则就不能异或,这一位答案就没有1,
开始被自己先搞了一发古怪的贪心搞过去了
然后网上看代码,比自己的将近短一半,
贪心思路也简单不少,学一学写法
代码1(借鉴)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=6e6+10;
//01字典树
int ch[maxn][2],num[maxn],tot;
int q,x;
char op[3];
void insert(int x,int op)//op=1加 op=1减
{
int rt=1;
for(int i=30;i>=0;--i)
{
int j=(x>>i&1)>0;//x在i位是否为1
if(!ch[rt][j])ch[rt][j]=++tot;
rt=ch[rt][j];
num[rt]+=op;
}
}
int query(int x)
{
int rt=1,ans=0;
for(int i=30;i>=0;--i)
{
int j=(x>>i&1)^1;//^1与==0等价
if(num[ch[rt][j]])//如果可异或,说明最后结果有这一位
{
ans|=(1<<i);
rt=ch[rt][j];
}
else rt=ch[rt][1-j];//不可异或,结果没有这一位
}
return ans;
}
void init()
{
memset(num,0,sizeof(num));
memset(ch,0,sizeof(ch));
tot=1;//root从1开始
}
int main()
{
while(scanf("%d",&q)==1)
{
init();
insert(0,1);//把0加进字典树
while(q--)
{
scanf("%s%d",op,&x);
if(op[0]=='+')insert(x,1);
else if(op[0]=='-')insert(x,-1);
else printf("%d\n",query(x));
}
}
return 0;
}
代码2(自己乱搞的)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=6e6+2e5;
struct node
{
int num,Next[2];
}trie[maxn];
int tot,root;
int n,m;
void insert(int r,int s[],int len,int op)
{
for(int i=0;i<len;i++)
{
//printf("%d",s[i]);
trie[r].num+=op;
if(trie[r].Next[s[i]]==0)trie[r].Next[s[i]]=++tot;
r=trie[r].Next[s[i]];
}
trie[r].num+=op;
}
int query(int r,int x,int s[],int len)
{
int now=31,res=0;
//puts(":");
//for(int i=0;i<len;++i)
//printf("%d",s[i]);
bool flag=0;
for(int i=0;i<len;i++)
{
if(s[i]==0&&trie[trie[r].Next[1]].num>0)//使答案变大,01
{
if(!flag)flag=1;
res+=(1<<now);
r=trie[r].Next[1];
}
else if(trie[trie[r].Next[0]].num>0)//不变但没变坏,10或00
{
r=trie[r].Next[0];
}
else if(flag)//为了高位而牺牲低位,走11
{
res+=(1<<now);
r=trie[r].Next[1];
}
else break;//第一位就要牺牲x 不值得
now--;
}
return res^x;
}
void init()
{
trie[0].num=tot=0;
root=++tot;
memset(trie,0,sizeof(trie));
}
char op[5];
int ans[35];
int main()
{
while(~scanf("%d",&n))
{
init();
for(int i=0;i<n;i++)
{
int x;
scanf("%s%d",op,&x);
int now=0;
for(int i=31;i>=0;--i)ans[now++]=(x>>i&1);
if(op[0]=='?')printf("%d\n",query(root,x,ans,now));
else if(op[0]=='+')insert(root,ans,now,1);
else if(op[0]=='-')insert(root,ans,now,-1);
}
}
return 0;
}
拓展
询问区间最大异或和。
把前缀异或和的值像这样插到01字典树里即可
即边处理前缀异或和边插入边处理询问。