bzoj 3261: 最大异或和
表示bzoj把本校大佬Cherish_OI当成僵尸用户(ORZ
现在都还在封我们学校IP(管理员怕是看不到我的blog。。。
OK那么这题又get一个新姿势,可持久化字典树。听说这东西解决异或和是常规操作?
这个东西呢,我第一感觉就是跟主席树很像,然后吐槽一波空间消耗极大
对于每个前缀异或和建树,然后前缀和,这里的前缀和是每个位0,1的数量(就是主席树嘛)
然后query的时候,因为答案是sum[p-1]^sum[n]^x,而sum[n]^x已知,那么就去字典树里l-1~r的区间由大到小去找和sum[n]^x的当前二进制位不同数是否存在,也就是if(当前区间c>0),就是记录答案。
细节:一开始要插一个0的数,因为当p取1,也就是如果我们把全部的数异或起来是最大的,那答案就是sum[n]^x,在前面多增加一位方便。并且如果不这么做,找的时候就要从l-2~r-1里面找,因为前缀和的缘故sum的总数是要比值的数量少1的。
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int Bin[30]; struct Trie { int w[2],sum; }tr[21000000];int trlen,rt[610000]; int maketree(int x,int d) { int y=++trlen;int ret=y; for(int i=23;i>=0;i--) { tr[y].w[0]=tr[x].w[0]; tr[y].w[1]=tr[x].w[1]; tr[y].sum=tr[x].sum+1; int t; if((d&Bin[i])==0)t=0; else t=1; x=tr[x].w[t]; tr[y].w[t]=++trlen; y=tr[y].w[t]; } tr[y].sum=tr[x].sum+1; return ret; } int query(int x,int y,int d) { int ans=0; for(int i=23;i>=0;i--) { int t; if((d&Bin[i])==0)t=0; else t=1; if(tr[tr[y].w[t^1]].sum-tr[tr[x].w[t^1]].sum>0) { ans+=Bin[i]; x=tr[x].w[t^1], y=tr[y].w[t^1]; } else x=tr[x].w[t], y=tr[y].w[t]; } return ans; } int a[610000],s[610000]; char ss[10]; int main() { Bin[0]=1;for(int i=1;i<=30;i++)Bin[i]=Bin[i-1]*2; int n,m; scanf("%d%d",&n,&m);n++; s[1]=0; for(int i=2;i<=n;i++) scanf("%d",&a[i]), s[i]=s[i-1]^a[i]; trlen=0; for(int i=1;i<=n;i++) rt[i]=maketree(rt[i-1],s[i]); int l,r,p; while(m--) { scanf("%s",ss+1); if(ss[1]=='A') { n++; scanf("%d",&a[n]), s[n]=s[n-1]^a[n]; rt[n]=maketree(rt[n-1],s[n]); } else { scanf("%d%d%d",&l,&r,&p); printf("%d\n",query(rt[l-1],rt[r],s[n]^p)); } } return 0; }
pain and happy in the cruel world.