字典树_异或和

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 }

以上是关于字典树_异或和的主要内容,如果未能解决你的问题,请参考以下文章

bzoj3261: 最大异或和 可持久化字典树模板

cf282 E. Sausage Maximization(字典树+前缀和)

Codeforces 979 D. Kuro and GCD and XOR and SUM(异或和,01字典树)

Adventure C CF-665E(字典树)

HDU - 6955 Xor sum(字典树+贪心)

HDOJ6955Xor sum(异或前缀和,01字典树)