链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2741
题解
比较套路的一个题。
首先肯定是要搞前缀异或和,然后问题就成了在一个区间里选出两个数字使得他们的异或值最大。
这个异或值最大是个套路,就直接用可持久化字典树贪心搞就行了。
这题主要是分块。
预处理出
f[i][j]
表示第
i
个块到第
代码
//可持久化字典树+分块
#include <cstdio>
#include <algorithm>
#define maxn 15000
#define maxs 500
using namespace std;
int N, M, a[maxn], f[200][200], ndtot, size, s[maxn][40], lastans;
struct trie{trie* ch[2];int size, tail, table;}pool[maxn*40], *nul, *root[maxn];
void ins(trie *pre, trie *now, int *s)
{
*now=*pre;
now->size++;
if(*s==-1)now->tail=0, now->table=*(s+1);
else ins(pre->ch[*s],now->ch[*s]=pool+ ++ndtot,s+1);
}
int find(trie *pre, trie *now, int *s)
{
int x=*s^1;
if(*s==-1)return now->table^*(s+1);
if(now->ch[x]->size-pre->ch[x]->size)return find(pre->ch[x],now->ch[x],s+1);
else return find(pre->ch[!x],now->ch[!x],s+1);
}
void init()
{
int i, x, j, ans;
scanf("%d%d",&N,&M);
nul=pool; nul->ch[0]=nul->ch[1]=nul;
s[0][31]=-1;
ins(nul,root[0]=pool+ ++ndtot,s[0]);
for(i=1;i<=N;i++)
{
scanf("%d",&x);
x^=s[i-1][32];
for(j=0;j<=30;j++)s[i][j]=(x>>j)&1;
for(j=0;j<=15;j++)swap(s[i][j],s[i][30-j]);
s[i][31]=-1, s[i][32]=x;
ins(root[i-1],root[i]=pool+ ++ndtot,s[i]);
}
for(size=1;(1<<size)*(1<<size)<N;size++);
for(i=0;i<=N>>size;i++)
{
x=i<<size; ans=0;
for(j=x;j<=N;j++)
{
if(x==0)ans=max(ans,s[j][32]), ans=max(ans,find(root[0],root[j],s[j]));
else ans=max(ans,find(root[x-1],root[j],s[j]));
if(j+1>>size!=j>>size)f[i][j>>size]=ans;
}
}
}
void solve(int l, int r)
{
int i, ans=0;
for(i=l;i>>size==l>>size and i<=r;i++)ans=max(ans,find(root[l],root[r],s[i]));
if(l>>size!=r>>size)
for(i=r;i>>size==r>>size;i--)ans=max(ans,find(root[l],root[r],s[i]));
ans=max(ans,f[(l>>size)+1][(r>>size)-1]);
printf("%d\n",lastans=ans);
}
int main()
{
int i, l, r;
init();
for(i=1;i<=M;i++)
{
scanf("%d%d",&l,&r);
l=((long long)l+lastans)%N+1, r=((long long)r+lastans)%N+1;
if(l>r)swap(l,r);
solve(l-1,r);
}
return 0;
}