【BZOJ 3261】最大异或和【可持久化字典树】
题意
给出一个长度为n的整数序列,给出m个操作。操作有两种。1,Ax表示在序列结尾增加x。2,Qlrx表示找到一个位置p满足 l<=p<=r,使得a[p] xor a[p+1]xor...xor a[n] xor x最大,并输出这个最大值。
分析
今天学可持久化字典树的时候的找的一道模板题。对于这个题目其实只要学过主席树应该都能自己写出来(我照着主席树的套路写然后debug一下午然后发现num数组想错了mmp)
我们定义sum[i]为a[1]xor a[2] xor ...xor a[i]。那么对于每个询问操作Qlrx,我们要找出一个[l,r]内的p使得sum[n]xor sum[p-1] xor x最大.而显然sum[n]xor x是一个常数,所以我们要找到一个p使得sum[p-1]xor某个常数最大。
字典树的经典用法就是在一堆数字中,查询某个与x异或最大的是哪个数字。但是这个问题中有区间限制,所以我们需要将其可持久化。与线段树一样,我们记录每个历史版本,也就是前i个数字组成的字典树,根为root[i]。建树的思路和主席树几乎是完全一样的。只不过我们为了实现查询,多更新了一个num数组。因为主席树查询时候可以直接相减得到这个区间内各个数的数量,但是字典树不行,所以我们多维护一个num,来确定在[l,r]区间内,这个节点有没有走向0或者1的方法。
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 6 7 using namespace std; 8 const int maxn=600000*24; 9 int n,m,sz; 10 int ch[maxn][2],a[maxn],root[maxn],sum[maxn],val[maxn],num[maxn][2]; 11 void update(int x,int y,int aa,int id){ 12 root[x]=++sz; 13 x=root[x]; 14 for(int i=23;i>=0;i--){ 15 int c=(aa>>i)&1; 16 num[x][c]+=num[y][c]+1; 17 num[x][!c]=num[y][!c]; 18 ch[x][!c]=ch[y][!c]; 19 ch[x][c]=++sz; 20 memset(ch[sz],0,sizeof(ch[sz])); 21 x=ch[x][c],y=ch[y][c]; 22 } 23 val[x]=id; 24 } 25 26 int query(int x,int y,int aa){ 27 for(int i=23;i>=0;i--){ 28 int c=(aa>>i)&1; 29 if(num[x][!c]-num[y][!c]) 30 x=ch[x][!c],y=ch[y][!c]; 31 else 32 x=ch[x][c],y=ch[y][c]; 33 } 34 return val[x]; 35 } 36 37 int main(){ 38 scanf("%d%d",&n,&m); 39 sz=1; 40 sum[0]=0; 41 update(0,0,0,0); 42 for(int i=1;i<=n;i++){ 43 scanf("%d",&a[i]); 44 sum[i]=sum[i-1]^a[i]; 45 update(i,root[i-1],sum[i],i); 46 } 47 char c; 48 int l,r,x; 49 for(int i=1;i<=m;i++){ 50 scanf(" %c",&c); 51 if(c=='A'){ 52 n++; 53 scanf("%d",&a[n]); 54 sum[n]=sum[n-1]^a[n]; 55 update(n,root[n-1],sum[n],n); 56 }else{ 57 scanf("%d%d%d",&l,&r,&x); 58 printf("%d\n",sum[query(root[r-1],root[l-2],x^sum[n])]^sum[n]^x); 59 } 60 } 61 return 0; 62 } 63 //LQL