题目:
题解:
我们对这个题目转化一下:
sum[l−1]
s
u
m
[
l
−
1
]
^
sum[r]
s
u
m
[
r
]
,每个询问就转化为在sum[l-1]到sum[r]之间找两个数,使其异或值最大。
问题的简化:在区间内找一个数,使它与另一个已知数的异或值最大。这个问题可以用可持久化Trie树解决,只需要在Trie树上走相异的节点,可以O(log)的级别求出来
对于现在的问题,我们先分块然后预处理f,f[i][j]表示以i为起点的块到j点异或最大值是多少,那么不难列出
f[i][j]=max(f[i][j−1],query((i−1)∗block+1,j,a[j]))
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
−
1
]
,
q
u
e
r
y
(
(
i
−
1
)
∗
b
l
o
c
k
+
1
,
j
,
a
[
j
]
)
)
然后对于每个询问,找到区间内第一个完整块的起始点x,答案赋值为f[pos[x]][r],然后对于左边剩余的点暴力计算并和答案取max。
预处理效率
O(nn−−√)
O
(
n
n
)
查询效率是
O(mn−−√log a[i])
O
(
m
n
l
o
g
a
[
i
]
)
代码:
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
using namespace std;
const int sz=30;
const int N=12005;
int ans,size,tot,n,sum[N*60],ch[N*60][2],a[N],root[N],block,m,pos[N],f[120][N];
void insert(int &now,int x,int dep)
{
sum[++size]=sum[now]+1; ch[size][0]=ch[now][0]; ch[size][1]=ch[now][1];
now=size;
if (dep==-1) return;
int k=(x>>dep)&1;
if (k) insert(ch[now][1],x,dep-1);
else insert(ch[now][0],x,dep-1);
}
void query(int l,int r,int x,int dep)
{
if (dep==-1) return;
int k=(x>>dep)&1;
if (sum[ch[r][k^1]]-sum[ch[l][k^1]]>0)
{
ans+=(1<<dep);
query(ch[l][k^1],ch[r][k^1],x,dep-1);
}else query(ch[l][k],ch[r][k],x,dep-1);
}
int main()
{
freopen("data.in","r",stdin);
int q;scanf("%d%d",&n,&q);
for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]^=a[i-1],root[i]=root[i-1],insert(root[i],a[i],sz);
block=sqrt(n);
m=n/block; if (n%block) m++;
for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
for (int i=1;i<=m;i++)
for (int j=(i-1)*block+1;j<=n;j++)
{
ans=0;query(root[(i-1)*block],root[j],a[j],sz);
f[i][j]=max(f[i][j-1],ans);
}
int answer=0;
while (q--)
{
int l,r,x,y;
scanf("%d%d",&x,&y);
l=(((LL)x+answer)%n)+1;
r=(((LL)y+answer)%n)+1;
if (l>r) swap(l,r);
l--;answer=0;
if (pos[l]==pos[r])
{
for (int i=l;i<=r;i++)
ans=0,query(root[l-1],root[r],a[i],sz),answer=max(answer,ans);
}
else
{
answer=f[pos[l]+1][r];
int up=min(n,pos[l]*block);
for (int i=l;i<=up;i++)
ans=0,query(root[l-1],root[r],a[i],sz),answer=max(answer,ans);
}
printf("%d\n",answer);
}
}