题目
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字典树里即可
即边处理前缀异或和边插入边处理询问。
本文深入探讨了可持久化字典树的数据结构及其在处理动态集合的异或最大值查询问题上的应用。通过具体题目解析,展示了如何利用01字典树支持元素的增删操作,并实现高效查询。提供了两种实现代码示例,对比了贪心策略的优劣,同时扩展讨论了区间最大异或和的处理方法。
1294

被折叠的 条评论
为什么被折叠?



