bzoj2741(分块+可持久化Trie)
题意中文我就不说了
解析: 分块+可持久化Trie,先得到前缀异或值,插入到Trie中,然后分块,对每一块,处理出dp[i][j](i代表第几块,j代表第几个位置),dp[i][j]代表以第i块开始的到j这个位置
的连续字串最大异或值。查询时,如果l,r不在同一块内,可以先查询l所在的块的后一个块到r的连续字串最大异或值,之前的dp就可以派上用场了,然后就是处理l到l所在块
的这段区间,取两者最大值即可。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; const int maxn=12005; const int maxbit=31; int N,M,A[maxn]; int tr[maxn]; struct PerTrie { int next[10000005][2],num[10000005]; int id; void init(){ id=next[0][0]=next[0][1]=num[0]=0; } int f(int x,int i){ return (x>>i)&1; } void Insert(int& rt,int pre,int x,int pos) //插入 { rt=++id; next[rt][0]=next[pre][0]; next[rt][1]=next[pre][1]; num[rt]=num[pre]+1; if(pos==-1) return; int d=f(x,pos); Insert(next[rt][d],next[pre][d],x,pos-1); } int MaxXor(int l,int r,int x) //查询最大异或值,因为A[i]保存 { //的是前缀异或值,所以得到的结果就是某一段区间的异或值 int ret=0; for(int i=maxbit;i>=0;i--) { int d=f(x,i); int a=next[l][d^1],b=next[r][d^1]; if(num[b]-num[a]>0) ret|=(1<<i),l=a,r=b; else l=next[l][d],r=next[r][d]; } return ret; } }PT; int block,num,bel[maxn],dp[120][maxn]; //dp保存第几块到第几个数的区间最大异或值 void init() { tr[0]=0; PT.init(); for(int i=1;i<=N;i++) PT.Insert(tr[i],tr[i-1],A[i],maxbit); //插入 block=(int)sqrt(N+0.5); num=N/block; if(N%block) num++; //加1 memset(dp,0,sizeof(dp)); bel[0]=0; for(int i=1;i<=N;i++) bel[i]=(i-1)/block+1; //记录下属于哪个块 for(int i=1;i<=num;i++) { int st=(i-1)*block+1; for(int j=st;j<=N;j++) { dp[i][j]=max(dp[i][j-1],A[j]^A[st-1]); //可能是[st,j]这段区间 dp[i][j]=max(dp[i][j],PT.MaxXor(tr[st-1],tr[j],A[j])); //再找最大的 } } } int GetAns(int l,int r) { l--; int s=bel[l],ret=0; if(bel[r]>s) ret=dp[s+1][r]; //查询从后面一个块开始的 for(int i=l;i<=min(r,s*block);i++) { ret=max(ret,PT.MaxXor(tr[l-1],tr[r],A[i])); } return ret; } int main() { scanf("%d%d",&N,&M); A[0]=0; int x; for(int i=1;i<=N;i++) { scanf("%d",&x); A[i]=A[i-1]^x; } init(); int last=0,l,r; while(M--) { scanf("%d%d",&l,&r); l=(l+(LL)last)%N+1; r=(r+(LL)last)%N+1; if(l>r) swap(l,r); //printf("%d %d\n",l,r); last=GetAns(l,r); printf("%d\n",last); } return 0; }