题意:
给出一个长度为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;
}