题意:
给出一个长度为n的序列;
m次查询区间[l,r]子区间的最大异或和;
强制在线;
n<=12000,m<=6000;
题解:
这诡异的数据范围以及诡异的复杂度。。
FOTILE——中国高端数据结构领导者;
如果不考虑区间问题,那么就可以用可持久化Trie树解决;
具体就是求出前缀区间和,插入到可持久化Trie里,然后就是选两个数异或和最大的问题了;
多了一个区间限制之后,如果暴力查,那么是O(n^2logn)的复杂度;
这里进行一下分块,预处理f[i][j]表示从第i块开头,到j这个元素区间的最大异或和是多少;
这个数组大小是n√n,而转移关系是f[i][j]=max(f[i][j-1],s[j]在[i-1,j]之间的最大异或和);
这样预处理的O(n√nlogn),询问时利用一下数组,后面的整段区间直接可以拿出来;
前面零碎的不好超过√n,所以总复杂度O((n+m)√nlogn);
int 是可以过的,注意强制在线那里可能会爆掉;
代码:
#include<math.h> #include<stdio.h> #include<string.h> #include<algorithm> #define N 12100 #define K 31 using namespace std; typedef long long ll; int s[N]; int next[N*K<<1][2],size[N*K<<1],tot; int root[N]; int f[200][N]; void Insert(int &no,int val,int deep) { int p=++tot; next[p][0]=next[no][0],next[p][1]=next[no][1]; size[p]=size[no]+1; no=p; if(deep<0) return ; Insert(next[no][!!(val&1<<deep)],val,deep-1); } int query(int nol,int nor,int val,int deep) { if(deep<0) return 0; bool index=!(val&1<<deep); if(size[next[nor][index]]-size[next[nol][index]]) return 1<<deep|query(next[nol][index],next[nor][index],val,deep-1); else return query(next[nol][!index],next[nor][!index],val,deep-1); } int calc(int l,int r,int end) { int ret=0; for(int i=l;i<end&&i<r;i++) { ret=max(ret,query(root[i],root[r],s[i],K)); } return ret; } int main() { int n,m,i,j,k,l,r,last,bk; scanf("%d%d",&n,&m); bk=sqrt(n*1.0); for(i=1,j=0;i<=n;i++) { scanf("%d",s+i); s[i]^=j; root[i]=root[i-1]; Insert(root[i],s[i],K); j=s[i]; } for(i=1;i<=bk+1;i++) { k=i*bk; for(j=k+1;j<=n;j++) { f[i][j]=max(f[i][j-1],query(root[k-1],root[j],s[j],K)); } } for(i=1,last=0;i<=m;i++) { scanf("%d%d",&l,&r); l=(last+l)%n+1,r=(last+r)%n+1; if(l>r) swap(l,r); l--; j=l%bk==0&&l?l/bk:l/bk+1; last=max(calc(l,r,bk*j),f[j][r]); printf("%d\n",last); last%=n; } return 0; }