01字典树小结-2017HZAU现场赛H-MathematicalGame
2017-05-01 18:31
375 查看
01字典树
想了解01字典树先要知道字典树(Trie树):http://blog.csdn.net/williamsun0122/article/details/71056547其实01字典树就可以看成是把一个数的二进制字符化后插入到一颗一般的字典树中。
一般01字典树用来解决区间异或和之类的问题。
异或的性质:
1. 交换律
2. 结合律,即(a^b)^c = a^(b^c))
3. 自反性,即x^x=0
4. x^0=x
其中运用最多的就是自反性。
有上述性质,对于区间异或和要知道如下性质:
XOR[l,r] = XOR[1,l-1] ^ XOR[1,r]
所以对于区间异或和之类的题目,比如求区间异或和的最大值,我们可以将r前的所有前缀异或和加入到一个01字典树中,然后查询一下[1,r]就可以得到以r为右边界的最大异或和区间。
在查询最大异或值时我们用贪心的策略,比如我们在字典树中查询10101的最大异或值。
我们从最高位即第5位开始查(我省略掉前面的0位),由于第5位是1(对于其它的任意数,我们设为idx),之后看字典树中有没有第5为是1^1(idx^1)的数,如果有就进入0(idx^1)的节点(贪心思想,即首先保证该位异或后值为1,使异或值尽可能大),没有就进入1(idx)节点,然后从高位到低位依次这样即可。
说了那么多,我们看一道例题代码感受一下就知道了。
2017HZAU现场赛H-MathematicalGame
题目链接:http://acm.hzau.edu.cn/problem.php?id=1206题意:有T组样例,每组样例给n个数,a1…an(n<=1000000)。求这n个数中最大异或和值的区间。有多个答案区间按字典序输出。
题解:把1-n的所有前缀异或和插入01字典树,然后按我上面说的区间异或的性质扫一遍就可以了。这题就还要注意一下区间按字典序输出,之前没看到这个,一直WA。具体看代码注释。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6+5; //数的最大个数 const int INF = 0x3f3f3f3f; int ch[maxn*32][2]; //int型是32位,所以32*maxn int idx[maxn*32],cnt; int ans,l,r; //ans保存最大异或和的值,l,r为其区间 void init() //01字典树的初始化 { memset(ch,0,sizeof(ch)); memset(idx,INF,sizeof(idx)); cnt=1; ans=0; l=r=0; } void trie_insert(int id,int x) //插入序号为id的前缀异或和x { int num=0; for(int i=31;i>=0;i--) { int tmp = (x>>i)&1; //从第32位开始,以下和字典树类似 if(!ch[num][tmp]) ch[num][tmp] = cnt++; num = ch[num][tmp]; } //保证同一异或和的序号最小,为之后字典树输出区间准备 //比如插入1,5后再插入3,5最后idx[num]=1 if(id<idx[num]) idx[num] = id; } void trie_query(int id,int x) { int num=0,m=0; //m是以id为右边界,数x的异或的最大值 //num是与x异或后为m的值的节点序号 for(int i=31;i>=0;i--) { int tmp = (x>>i)&1; if(ch[num][tmp^1]) { num = ch[num][tmp^1]; m += (1<<i); } else { num = ch[num][tmp]; m += (0<<i); } } if(m>ans) //如果m大于当前最大异或和的值,直接更新区间和最大异或和值 { l = idx[num]; r = id; ans = m; } if(m==ans) //如果等于,区间按字典序更新 { if(idx[num]<l) { l = idx[num]; r = id; } if(idx[num]==l) { if(id<r) { l = idx[num]; r = id; } } } } int main() { //freopen("std.in","r",stdin); //freopen("out.txt","w",stdout); int T,n,num,tmp; scanf("%d",&T); for(int ca=1;ca<=T;ca++) { init(); trie_insert(0,0); num=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&tmp); { num ^= tmp; //num为i为前缀的异或和 trie_insert(i,num); trie_query(i,num); //查询以i为右边界的最大异或和值的区间 } } printf("Case #%d:\n%d %d\n",ca,l+1,r); } return 0; }
相关文章推荐
- 树状数组 逆序对变形 2017HZAU现场赛G-Sequence Number
- 2017 08 20 小结
- 2017 Wuhan University Programming Contest 现场赛G. Room
- 2017 Wuhan University Programming Contest 现场赛 K.Wifi Relay(最短路变形)
- 2017 Wuhan University Programming Contest 现场赛 I. A simple math problem(矩阵快速幂)
- 2017华为面试算法题小结
- 2017汉柏科技校园招聘(现场笔试)
- 2017 08 12 小结
- 2017 Wuhan University Programming Contest 现场赛G. Room
- 2017 Wuhan University Programming Contest 现场赛 K.Wifi Relay(最短路变形)
- 2017 Wuhan University Programming Contest 现场赛 I. A simple math problem(矩阵快速幂)
- 2017 10 03 小结
- 关于现场软件安装的几点经验小结
- 2017 Wuhan University Programming Contest 现场赛G. Room
- 2017 Wuhan University Programming Contest 现场赛 K.Wifi Relay(最短路变形)
- 2017 Wuhan University Programming Contest 现场赛 I. A simple math problem(矩阵快速幂)
- CCAI 2017 | 谭铁牛院士现场致辞:人工智能新热潮下要保持清醒头脑_设定科学的目标
- 2017 ACM 区域赛青岛站(现场赛) K Our Journey of Xian Ends
- ACM现场赛天津站小结
- {小结}GDOI2017 论自卑心理的产生