(字典树)codeforces - 706D Vasiliy's Multiset

原题链接:http://codeforces.com/problemset/problem/706/D



题意:需要你模拟一个多重集合(初始拥有0),该集合拥有三个功能

1、+ x  增加一个x(可以同时存在多个x,因为是多重集合)

2、-  x 去掉一个x

3、? x  输出在这个集合中和x异或最大的值。



分析:有20万个操作,很容易想到树形数据结构,我们可以看到每个x最多只有10^9,而1<<30比10^9大一点,所以10^9次方最多就是29位的01串,可以维护一个字典树。

每个节点有两个分支,0和1,深度最深也就是29。

我一开始想到把01串倒着往里插,因为在x进行&和>>1操作可以很方便达到这一点,但是+和-操作如果这样实现,就很难实现?操作了。

因为根据异或运算就是对应位相同为0,不同为1,而在01串中,越往左的1在10进制中的值就越大。

要取得异或的最大值,很容易想到就是贪心,从最左边开始比较,每次取和自己不同的那个分支,往下走,如果与自己不同的那个分支未曾建立过,或者统计的cnt已经==0(-操作删除了)就走相同的那一分支。

同时我们能够发现可以直接正着插,也就是说不管你的有效01串有几位,都把29位包含前导0的01串全都存进去。这就方便了?操作。



代码:

  1 #include <set>
  2 #include <map>
  3 #include <list>
  4 #include <cmath>
  5 #include <queue>
  6 #include <vector>
  7 #include <bitset>
  8 #include <string>
  9 #include <cctype>
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <cstdlib>
 13 #include <iostream>
 14 #include <algorithm>
 15
 16 using namespace std;
 17
 18 typedef long long ll;
 19 typedef unsigned long long ull;
 20 #define inf (0x3f3f3f3f)
 21 #define lnf (0x3f3f3f3f3f3f3f3f)
 22 #define eps (1e-8)
 23 int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;}
 24
 25 //--------------------------
 26
 27
 28 struct  Trie_node {
 29     int cnt;
 30     int next[2];
 31 }tree[10000010];
 32 int nxt;
 33
 34 int  createTrieNode() {
 35     memset(&tree[nxt], 0, sizeof(Trie_node));
 36     return nxt++;
 37 }
 38
 39 void Trie_insert(int val) {
 40     int rt=0;
 41     char bir[30];
 42     for(int i=29;i>=0;i--){
 43         bir[i]=(val&1)+‘0‘;
 44         val>>=1;
 45     }
 46     for(int i=0;i<30;i++){
 47         int id=bir[i]-‘0‘;
 48         if(!tree[rt].next[id]){
 49             tree[rt].next[id]=createTrieNode();
 50         }
 51         rt=tree[rt].next[id];
 52         tree[rt].cnt++;
 53     }
 54 }
 55
 56 void Trie_remove(int val){
 57     int rt=0;
 58     char bir[30];
 59     for(int i=29;i>=0;i--){
 60         bir[i]=(val&1)+‘0‘;
 61         val>>=1;
 62     }
 63     for(int i=0;i<30;i++){
 64         int id=bir[i]-‘0‘;
 65         rt=tree[rt].next[id];
 66         tree[rt].cnt--;
 67     }
 68
 69 }
 70
 71 int Trie_search(int val) {
 72     int rt=0;
 73     char res[30]={0};
 74     int ans=0;
 75     char bir[30];
 76     for(int i=29;i>=0;i--){
 77         bir[i]=(val&1)+‘0‘;
 78         val>>=1;
 79     }
 80     for(int i=0;i<30;i++){
 81         int id=bir[i]-‘0‘;
 82         if(tree[rt].next[id^1]&&tree[tree[rt].next[id^1]].cnt>0){
 83             rt=tree[rt].next[id^1];
 84             res[i]=‘1‘;
 85         }
 86         else{
 87             rt=tree[rt].next[id];
 88             res[i]=‘0‘;
 89         }
 90     }
 91     int k=0;
 92     for(;k<30&&res[k]==‘0‘;){
 93         k++;
 94     }
 95     for(int i=29;i>=k;i--){
 96         ans+=(1<<(29-i))*(res[i]-‘0‘);
 97     }
 98     return ans;
 99 }
100
101
102 void init(){
103     nxt=1;
104     memset(&tree[0],0,sizeof(Trie_node));
105 }
106
107 char op[2];
108 int v;
109
110 void solve() {
111     init();
112     Trie_insert(0);
113     int t;
114     scanf("%d",&t);
115     while(t--){
116         scanf("%s%d",op,&v);
117         if(op[0]==‘+‘)Trie_insert(v);
118         else if(op[0]==‘-‘)Trie_remove(v);
119         else if((op[0]==‘?‘))printf("%d\n",Trie_search(v));
120     }
121 }
122
123
124
125
126 int main() {
127
128 #ifndef ONLINE_JUDGE
129     freopen("in.txt", "r", stdin);
130     //freopen("out.txt", "w", stdout);
131 #endif
132     //iostream::sync_with_stdio(false);
133     solve();
134     return 0;
135 }

(字典树)codeforces - 706D Vasiliy's Multiset

时间: 2024-10-07 06:30:37

(字典树)codeforces - 706D Vasiliy's Multiset的相关文章

CodeForces 706D Vasiliy&#39;s Multiset (字典树查询+贪心)

题意:最开始的时候有一个集合,集合里面只有一个元素0,现在有q次操作,操作分为3种: + x: 表示向集合中添加一个元素x - x:表示删除集合中值为x的一个元素 ? x:表示查询集合中与x异或的最大值为多少 析:这是一个字典树的应用,不过确实没看出来....主要思想是这样,先用10进制数,转成二进制数,记下每个结点的0,1的个数,这样增加和删除,就是对01的删除, 剩下的就是查询,那么尽量让0和1XOR是最大的,所以,对于给定数,我们要去尽量他的XOR数,如果找到就加上,找不到,就找下一个.这

Codeforces 706D Vasiliy&#39;s Multiset(可持久化字典树)

[题目链接] http://codeforces.com/problemset/problem/706/D [题目大意] 要求实现一个集合中的三个操作,1:在集合中加入一个元素x,2:从集合中删除一个元素x(保证x存在),3:要求从集合中选出一个数,使得其与给出的数x的异或值最大,输出这个异或值. [题解] 可以将所有的以二进制形式存在01字典树上,删除即插入权值为-1的二进制串,对于异或值最大的操作,我们只要在字典树上按位贪心,从最高位开始尽量保证该位存在最后就能得到答案.写代码的时候直接写了

CodeForces 706D Vasiliy&#39;s Multiset

字典树. 比较经典的题目了.把每一个数字都插入到字典树中,询问的时候如果$x$的第$i$位是$p$,那么尝试着在字典树上往$pXOR1$的节点走下去,没有$pXOR1$节点的话再走$p$的.删除操作的话可以记录一下每一个节点出现了几次,$Insert$的时候$s[p].cnt++$,$Delete$的时候$s[p].cnt--$,如果发现$s[p].cnt==0$,那么将以$p$为根的树全删了,也就是$p$的$father$到$p$的路标为$-1$. #pragma comment(linker

C. Watto and Mechanism 字典树 Codeforces Round #291 (Div. 2)

C. Watto and Mechanism time limit per test 3 seconds memory limit per test 256 megabytes input standard input output standard output Watto, the owner of a spare parts store, has recently got an order for the mechanism that can process strings in a ce

[字典树] Codeforces 557E Ann and Half-Palindrome

题意: 给一个长度为5000的ab串,问你第k大的半回文子串是什么 所谓的半回文串就是下标是奇数的位置前后相等就好了. 思路: 首先发现串的长度只有5000,可以做一个类似区间dp的预处理 处理出dp[i][j]代表第i到j子串是不是半回文子串 然后依次把原串的所有子串插入字典树,并在节点标记个数 然后最后dfs一下k个就好了 代码: #include"cstdlib" #include"cstdio" #include"cstring" #in

Vasiliy&#39;s Multiset CodeForces -706D || 01字典树模板

就是一个模板 注意这题有一个要求:有一个额外的0一直保持在集合中 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int _LEN=30; 5 //上限为2^30-1,即二进制最多30位 6 int lft[40]; 7 namespace Trie 8 { 9 int ch[7001000][2],sz[7001000]; 10 int mem; 11 void insert(int

trie树 Codeforces Round #367 D Vasiliy&#39;s Multiset

1 // trie树 Codeforces Round #367 D Vasiliy's Multiset 2 // 题意:给一个集合,初始有0,+表示添加元素,-去除元素,?询问集合里面与x异或最大的值 3 // 思路:思路很好想,建立trie树,再贪心当前位是1则选0,0则选1 4 5 6 #include <bits/stdc++.h> 7 using namespace std; 8 #define LL long long 9 const double inf = 123456789

D. Vasiliy&#39;s Multiset 异或字典树

D. Vasiliy's Multiset time limit per test 4 seconds memory limit per test 256 megabytes input standard input output standard output Author has gone out of the stories about Vasiliy, so here is just a formal task description. You are given q queries a

Codeforces Round #367 (Div. 2) D. Vasiliy&#39;s Multiset

题目链接:Codeforces Round #367 (Div. 2) D. Vasiliy's Multiset 题意: 给你一些操作,往一个集合插入和删除一些数,然后?x让你找出与x异或后的最大值 题解: trie树xjb搞就行,每次要贪心,尽量满足高位为1. 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;i++) 3 using namespace std; 4 5 namespace trie 6 {