2741: 【FOTILE模拟赛】L
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 2939 Solved: 850
[ Submit][ Status][ Discuss]
Description
r = max ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).
Input
Output
Sample Input
1 4 3
0 1
0 1
4 3
Sample Output
7
7
HINT
HINT
N=12000,M=6000,x,y,Ai在signed longint范围内。
Source
这道题是裸的区间最大连续异或和,因为题目就让你求这个对吧...
考虑求前缀异或和,那么sum[L-1]^SUM[R]就是L到R得连续异或和,因为我们sum存储的是前缀,又因为相同的数异或为0,且sum[R]包含了sum[L-1],所以两者异或起来,1~L-1的异或贡献被抵消,就只剩下了L~R的贡献.那么问题就转化成为,在L-1到R内选择两个数的sum值异或起来最大.
考虑对于一个数 x 的sum,求与他之前的某个数异或起来最大,那么贪心的肯定让高位的1得到保证,由异或的性质可得我们要从高位到低位找到一个数的高位到低位的每一位尽量与x的每一位相反.那么我们对于序列上的每个点i就建立可持久化trie树维护1~i的sum值,trie树里面保存了每个数的二进制值,从高位到低位深度变深,这是一个01trie树.那么R的trie树-(L-1)trie的trie树就得到维护了L~R sum值 的01trie,那么对于这个区间里的每个数的sum就可以顺着这个01trie从高位到低位贪心选择与当前数的sum值的这一位不相同的,就可以找到最大值.但是这样对于每个询问是o(区间长度*trie树树高)的,那么我们就分块预处理处理每个块到每个点的答案(数据不大,所以可以空间允许精确到每个点),然后对于L到R包含的块的答案就能o(1)得到,不在块内的暴力查trie即可,可以结合代码理解.
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=12005;
const int maxbit=31;
inline const int read(){
register int f=1,x=0;
register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return f*x;
}
int a[maxn],c[maxn*1000][2],sum[maxn*1000],init[200][maxn],tr[maxn],st,n,m,block,blo[maxn],id,Whereever_I_Go,ed,_wanna,maxx,lastans;
struct Trie{
void insert(int &rt,int pre,int who,int pos){
rt=++id;
c[rt][0]=c[pre][0];c[rt][1]=c[pre][1];
sum[rt]=sum[pre]+1;
if(pos==-1) return;
Whereever_I_Go=(who>>pos)&1;
insert(c[rt][Whereever_I_Go],c[pre][Whereever_I_Go],who,pos-1);
}
inline int query(int l,int r,int who){
ed=0;
for(int i=maxbit;i>=0;i--){
_wanna=((who>>i)&1)^1;
int p=c[l][_wanna],q=c[r][_wanna];
if(sum[q]>sum[p]) ed|=(1<<i),l=p,r=q;
else l=c[l][_wanna^1],r=c[r][_wanna^1];
}
return ed;
}
}trie;
int main(){
n=read(),m=read(),block=(int)sqrt(n);tr[0]=0;
for(register int i=1;i<=n;i++) a[i]=read(),a[i]^=a[i-1];
for(register int i=1;i<=n;i++) trie.insert(tr[i],tr[i-1],a[i],maxbit);
for(register int i=1;i<=n;i++) blo[i]=(i-1)/block+1;
maxx=n/block+(n%block);
for(int i=1;i<=maxx;i++){
st=(i-1)*block+1;
for(int j=st;j<=n;j++){
init[i][j]=max(init[i][j-1],a[j]^a[st-1]);
init[i][j]=max(init[i][j],trie.query(tr[st-1],tr[j],a[j]));
}
}
int l,r;
while(m--){
l=read(),r=read();
l=(l+(long long)lastans)%n+1;
r=(r+(long long)lastans)%n+1;
if(l>r) swap(l,r);l--;
lastans=(blo[r]>blo[l])?init[blo[l]+1][r]:0;
int lim=min(blo[l]*block,r);
for(int i=l;i<=lim;i++) lastans=max(lastans,trie.query(tr[l-1],tr[r],a[i]));
printf("%d\n",lastans);
}
}