[BZOJ3261]最大异或和
题目大意:
给定一个非负整数序列{a},初始长度为N。
有M个操作,有以下两种操作类型:
1. A x:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。
2. Q l r x:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
a[p] xor a[p+1] xor ... xor a[N] xor x 最大,输出最大是多少。
解题思路:
首先,a[1] xor a[2] xor ... xor a[N] xor x很容易求出。
然后,异或满足交换律和结合律。
此处询问的是后缀,我们考虑转化成前缀异或。
设s[i]表示a[1] xor a[2] xor ... xor a[i]
即在\([l-1,r-1]\)中找一个数p,使s[N] xor x xor s[p]最大。
用可持久化Trie维护前缀异或信息,和节点个数,然后在区间内进行前缀差查询(可持久化Trie满足可加性),每次贪心地使高位尽可能大即可。
于是长得和主席树差不多。
毒瘤数据结构大法好o((⊙﹏⊙))o
C++ Code:
#include<bits/stdc++.h> int n,m,s[600006],b[25],cnt=0,ans,rt[600006]; struct node{ int ch[2],c; }d[22000006]; void add(int&o,int&pr,int nw){ o=++cnt; if(!~nw){d[o].c=d[pr].c+1;return;} d[o].ch[0]=d[pr].ch[0]; d[o].ch[1]=d[pr].ch[1]; add(d[o].ch[b[nw]],d[pr].ch[b[nw]],nw-1); d[o].c=d[d[o].ch[0]].c+d[d[o].ch[1]].c; } void ask(int&R,int&L,int nw){ if(!~nw)return; int _1=b[nw]^1; if(d[d[R].ch[_1]].c>d[d[L].ch[_1]].c){ ans|=1<<nw; ask(d[R].ch[_1],d[L].ch[_1],nw-1); }else ask(d[R].ch[b[nw]],d[L].ch[b[nw]],nw-1); } inline int readint(){ int c=getchar(),d=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^'0'); return d; } int main(){ n=readint(),m=readint(); s[0]=0; for(int i=1;i<=n;++i){ int q=readint(); s[i]=s[i-1]^q; for(int j=24;~j;--j) b[j]=(s[i]>>j)&1; add(rt[i],rt[i-1],24); } while(m--){ char c[2]; scanf("%s",c); if(c[0]=='A'){ int q=readint(); ++n; s[n]=s[n-1]^q; for(int j=24;~j;--j) b[j]=(s[n]>>j)&1; add(rt[n],rt[n-1],24); }else{ ans=0; int l=readint(),r=readint(),x=readint(),p; p=s[n]^x; if(r==1){ printf("%d\n",p); continue; } for(int j=24;~j;--j) b[j]=(p>>j)&1; ask(rt[r-1],l-2>0?rt[l-2]:rt[0],24); printf("%d\n",ans); } } return 0; }