Codeforces Round #173 (Div. 2) E. Sausage Maximization(字典树)
题目大意
转化后是这样的:给了一个长度为 n(1≤n≤105) 的数组,求一个不相交的前缀和后缀,使得这个前缀和后缀中的所有数的异或值最大
做法分析
如果这种题目没见过类似的话,感觉挺神的,一个长度为 105 的数组,怎么去选前缀和后缀?不过不要惊慌,题目出出来是给我们做的,总有一线生机!
先从最暴力的开始讲起:枚举每一个后缀,让他和所有不与之相交的前缀求异或值,那么转化成了:
给一个数 a,还有一堆数,怎么在这一堆数中找出一个数 b,a 和 b 的异或值最大?
想想:肯定要先把 a 和这一堆数转化成二进制数,枚举 a 的最高位,要使异或值最大,那么 b 从最高位开始,就要尽量与 a 对应的位不同,对不对?
想到这里,那就好办了:把这一堆数装进一个字典树中,当然,是从最高位开始装,然后就是在这一棵字典树中尽量找出与 a 当前位不同的数,直到找到最低位为止,那么当前路径上经过的数就是我们的数 b 了,a 异或 b 也一定是最大的
上面的问题解决了,这道题也就迎刃而解了:先把所有的数异或起来,作为最初的后缀,而前缀是 0,先插入字典树中,然后,每次将后缀“减一”,前缀“加一”,先把前缀加入字典树中,再在字典树中查询与当前的后缀异或值最大的数
总的时间复杂度 64*n
参考代码
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 typedef long long LL; 8 const int N=100006; 9 10 LL A[N], ans, cur, New; 11 int n; 12 13 struct Trie_Tree 14 { 15 struct node 16 { 17 int next[2]; 18 void init() 19 { 20 next[0]=next[1]=-1; 21 } 22 } T[64*N]; 23 int tot; 24 25 void Insert(LL val) 26 { 27 for(int i=63, u=0; i>=0; i--) 28 { 29 int id=(((1LL)<<i)&(val))!=0; 30 if(T[u].next[id]==-1) 31 { 32 T[tot].init(); 33 T[u].next[id]=tot++; 34 } 35 u=T[u].next[id]; 36 } 37 } 38 39 LL Find(LL val) 40 { 41 LL res=0; 42 for(int i=63, u=0; i>=0; i--) 43 { 44 int id=(((1LL)<<i)&(val))==0; 45 if(T[u].next[id]==-1) id^=1; 46 res=res*2LL+(LL)id; 47 u=T[u].next[id]; 48 } 49 return res; 50 } 51 } tree; 52 53 int main() 54 { 55 scanf("%d", &n); 56 cur=0, New=0; 57 for(int i=0; i<n; i++) scanf("%I64d", &A[i]), cur^=A[i]; 58 tree.T[0].init(), tree.tot=1, ans=cur; 59 tree.Insert(0LL); 60 for(int i=0; i<n; i++) 61 { 62 New^=A[i], cur^=A[i]; 63 tree.Insert(New); 64 LL temp=tree.Find(cur); 65 ans=max(ans, temp^cur); 66 } 67 printf("%I64d\n", ans); 68 return 0; 69 }
题目链接 & AC通道
Codeforces Round #173 (Div. 2) E. Sausage Maximization