字典树_异或和
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字典树_异或和相关的知识,希望对你有一定的参考价值。
参考技术A 2018-03-16异或的性质
有上述性质,对于区间异或和要知道如下性质:
XOR[l,r] = XOR[1,l-1] ^ XOR[1,r]
异或的知识还有很多有趣的解释和应用,详情可以移步知乎。
(CodeForces - 948D)Perfect Security
题意:先给你一串数字a[n],然后再给你一串数字b[n],让你任意排列b[n],使得a[i]^b[i]的值的输出字典序最小。
思想:想办法让一个数字k异或后的结果最小,其实就是想办法让该数字二进制高位变为0,而由上面异或的性质可以知道,其实就是在另一串数字中找到一个数字d,使得数字d的二进制与数字k的二进制在高位上尽可能多得相同,于是就有了两个方法。
方法一:暴力寻找(??)
从x的高位找,如果a[n]中的数字最大值还不能达到高位,或者大于x的最小数字在二进制中甚至比x还高一位,那么异或取消。
还有一个有趣的一点就是:怎么确认我找到的ans一定是在b[n]呢——其实关键点就在lower_bound这里,举个例子就能理解。
方法二:常规01字典树。O(n*32(字典树深度))
CodeForces 665E Beautiful Subarrays
题目:Beautiful Subarrays
链接:Here
题意:给一个数组,给一个 ‘完美区间‘ 的定义:l 到r 区间内的所有数异或和大于等于k,问给定数组有多少完美区间。
思路:
异或运算可以前缀和处理,用w[i]表示i 前面的数异或和,那么w[5]^w[3]就是4、5两数异或的值。
现在我们要开始建字典树了(感觉异或和字典树老扯上关系),我们从前往后一个个地把前缀加入字典树(补齐,高位补0),在w[i]加入字典树前,判断w[i]和前面的哪个前缀可以异或并且值大于等于k。这里要用树形DP,根据性质,假设高位异或后,在那一位>k在那一位的值,那就不用往下继续了,因为高位有差别,大小就已经判断出来了,直接加上所有以这个为前缀的w[i]的数量就可以了,细节小心处理,大概就是这样。
AC代码:(没整理,乱七八糟的)
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<math.h> 5 #include<set> 6 #include<map> 7 #include<list> 8 #include<stack> 9 #include<queue> 10 #include<vector> 11 #include<string> 12 #include<iostream> 13 #include<algorithm> 14 using namespace std; 15 #define lson rt<<1 16 #define rson rt<<1|1 17 #define N 1000010 18 #define M 100010 19 #define Mod 1000000007 20 #define LL long long 21 #define INF 0x7fffffff 22 #define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;i++) 23 #define For(i,f_start,f_end) for(int i=f_start;i<f_end;i++) 24 #define REP(i,f_end,f_start) for(int i=f_end;i>=f_start;i--) 25 #define Rep(i,f_end,f_start) for(int i=f_end;i>f_start;i--) 26 #define MT(x,i) memset(x,i,sizeof(x)) 27 #define gcd(x,y) __gcd(x,y) 28 const double PI = acos(-1); 29 30 struct Node 31 { 32 int num; 33 int next[2]; 34 }v[N*33]; 35 int vNum; 36 37 int c[33],co; 38 LL ans; 39 40 void add(int x) 41 { 42 int bt[33],bo=0; 43 while(x) 44 { 45 bt[bo++] = x&1; 46 x>>=1; 47 } 48 for(int i=bo;i<33;i++) 49 { 50 bt[i]=0; 51 } 52 int p=0,i; 53 for(i=32;i>=0;i--) 54 { 55 int pos = !bt[i]; 56 57 if(v[p].next[pos]!=-1) 58 { 59 if(c[i]==0) ans+=v[v[p].next[pos]].num; 60 else 61 { 62 p = v[p].next[pos]; 63 if(p==-1) break; 64 continue; 65 } 66 } 67 else if(v[p].next[!pos]==-1) break; 68 if(c[i]==1) break; 69 p = v[p].next[!pos]; 70 if(p==-1) break; 71 } 72 if(i==-1) ans+=v[p].num; 73 p=0; 74 for(int i=32;i>=0;i--) 75 { 76 int pos = bt[i]; 77 if(v[p].next[pos]==-1) 78 { 79 v[vNum].next[0]=v[vNum].next[1]=-1; 80 v[vNum].num=0; 81 v[p].next[pos]=vNum++; 82 } 83 p = v[p].next[pos]; 84 v[p].num++; 85 } 86 } 87 88 int w[N]; 89 int main() 90 { 91 int n,k,x; 92 while(~scanf("%d%d",&n,&k)) 93 { 94 int ttt=k; 95 w[0]=0; 96 for(int i=1;i<=n;i++) 97 { 98 scanf("%d",&x); 99 w[i]=w[i-1]^x; 100 } 101 co=0; 102 while(k) 103 { 104 c[co++]=k&1; 105 k>>=1; 106 } 107 for(int i=co;i<33;i++) c[i]=0; 108 v[0].next[0]=v[0].next[1]=-1; 109 v[0].num=0; 110 vNum=1; 111 ans=0; 112 k=ttt; 113 for(int i=1;i<=n;i++) 114 { 115 add(w[i]); 116 if(w[i]>=k) ans++; 117 } 118 printf("%I64d\n",ans); 119 } 120 return 0; 121 }
以上是关于字典树_异或和的主要内容,如果未能解决你的问题,请参考以下文章
cf282 E. Sausage Maximization(字典树+前缀和)