您的位置:首页 > 其它

Codeforces Round #367 (Div. 2) D. Vasiliy's Multiset(字典树模板)

2016-08-13 21:17 330 查看
这里戳题目

题意:首先给你一个集合,这个集合里面一开始只有零(一开始是有零的,别用个空的集合,并且这个集合允许有重复的值),然后有三种操作,如果输入时加号,就往这个集合新增一个数字,如果是减号,就把减号后的那个数字从集合里面去掉,如果是问号,就从集合里面找一个数,这个数字和问号后面的数字异或的结果是最大的。

异或:意思是说有两个数字,如果不是二进制,就转化成二进制,然后两两比较,相同为零,不同为一,再转化成自己想要的进制就行了,在代码中,a和b异或就是a^b

思路:其实如果只是增删,直接就是一个map或者什么之类的就行了,然而这个要搞异或,如果直接查找肯定超时,所以这里要用到字典树,可以说是一个字典树的模板题。首先是将要增加进去的放到字典树里面去,然后是删除,再来是异或。其实难点在异或,异或的时候,如果你想要得到异或的最大值,那么就要不断地找不同的,所以在建立数组的时候,我是开了个二维的,只有两列,一个是0,一个是1,因为是二进制嘛,所以用这个来查找不一样的。

字典树:其实以前也遇到过关于异或的题目,然而因为看不懂所以没看,现在遇到了,就顺便了解了下字典树。字典树,如字面意思,根据前面已有的字母或数字来查找你想要的,也时根据已有的字母或数字来增加你想要的数据。原理就是相同的前缀。而且他们的每个节点都有一个特定的编号,如此便不会搞混,就像是有一个父节点,然后有多条链延伸出来一样。所以如果你要像这道题一样删除数据,就还要新建一个数组来记录这个节点用了多少次。一开始我是一脸懵逼的,但是做完这道题,感触良多啊。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define ll long long
#define maxn 100010
#define PI acos(-1.0)    //圆周率
const ll INF = 1e9;
const int len = 31;
using namespace std;
struct node
{
int sz;
int ch[maxn*45][2];
int sum[maxn*45];
void trie()
{
sz=1;
memset(ch,0,sizeof(ch));
memset(sum,0,sizeof(sum));
}

void add(int x)
{
int u=0;
bool f;
for(int i=len;i>=0;i--)
{
f=x&(1<<i);
if(ch[u][f]==0)  ch[u][f]=sz++;
u=ch[u][f];
sum[u]++;           //每个节点都是唯一的,所以不需要开二维数组
}
}

void del(int x)
{
int u=0;
bool f;
for(int i=len;i>=0;i--)
{
f=x&(1<<i);
u=ch[u][f];
sum[u]--;
}
}

int query(int x)
{
int u=0;
int v=0;
bool f;
for(int i=len;i>=0;i--)
{
f=x&(1<<i);
if(sum[ch[u][!f]])  f=!f; //尽量找二进制里面和X不一样的,异或的结果才能尽量的大
v=v|(f<<i);
u=ch[u][f];
}
return v;
}
}st;
int n;
int main()
{
scanf("%d",&n);
st.trie();
st.add(0);               //注意这道题一开始就有一个零在里面
char a[2];
int x;
for(int i=0;i<n;i++)
{
scanf("%s",a);
scanf("%d",&x);
if(a[0]=='+')  st.add(x);
else if(a[0]=='-')  st.del(x);
else if(a[0]=='?')
{
int ans=x^st.query(x);
printf("%d\n",ans);
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  codeforces