关闭

Codeforces 706D-Vasiliy's Multiset(Trie树)

123人阅读 评论(0) 收藏 举报
分类:

题目链接请戳这里http://www.codeforces.com/problemset/problem/706/D
题目大意:
有一个允许多元素重复的集合A,集合初始的时候只有0。
输入一个整数q(1q200000),代表q个操作,每个操作有三种情况:
+ x:向集合中增加数x(1x109
- x:删除数x(确保x一定在集合中)
? x:求max xy,yA
题目分析:
一开始没有思路,通过百度发现涉及异或最大值的题都是将数据按从高到低位放在Trie树中,那么+和-操作就很好办了(Trie树就是按前缀保存的),?操作也好办,既然要求异或值最大,就从高到低往下搜索,使得与x相反的位尽可能多,一旦Trie树中存在与x对应位相反的前缀,就更新答案ans+=1<<i(其中i为位数)。
其实微软有一道机试题是跟子网段IP地址有关的,那道题是把IP地址的点分式拆成二进制形式,从高到低用Trie树维护,那么很显然Trie树的一个节点就是一个子网!,那道题中提到的admit和deny操作也就对应Trie树的添加前缀和删除前缀……

下面的ac代码可以用作Trie树的模板,将二叉树改为26叉树就可以适用于传统学习到的描述字母的Trie树。

#include <bits/stdc++.h>
using namespace std;

struct Trie {
  Trie* next[2];
  int cnt;
  Trie() {
    memset(next,0,sizeof(next));
    cnt=0;
  }
};
Trie* tr;
void insert(int n) {
  Trie *p=tr;
  for(int i=30;i>=0;i--) {
    int j=(n&(1<<i))?1:0;
    if(p->next[j]==NULL)
      p->next[j] = new Trie();
    p=p->next[j];
    p->cnt++;
  }
}
void del(int n) {
  Trie *p=tr;
  for(int i=30;i>=0;i--) {
    int j=(n&(1<<i))?1:0;
    p=p->next[j];
    p->cnt--;
  }
}
int query(int n) {
  int ans=0;
  Trie *p=tr;
  for(int i=30;i>=0;i--) {
    int j=(n&(1<<i))?0:1;
    if(p->next[j] && p->next[j]->cnt) {
      ans+=1<<i;
      p=p->next[j];
    }
    else {
      if(j==1)
        p=p->next[0];
      else
        p=p->next[1];
    }
  }
  return ans;
}

int main() {
  int q;
  tr = new Trie();
  insert(0);
  cin>>q;
  while(q--) {
    char c;
    int x;
    cin>>c>>x; //这个很慢!!!!
    if(c=='+')
      insert(x);
    else if(c=='-')
      del(x);
    else
      printf("%d\n", query(x));
  }
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:56523次
    • 积分:3205
    • 等级:
    • 排名:第10668名
    • 原创:270篇
    • 转载:53篇
    • 译文:0篇
    • 评论:7条
    文章分类
    最新评论