摘要
给定序列, 若干个询问, 给定 \(l,r\), 求 \(a[i]\) \(xor\) \(a[i+1]\) \(xor\) \(···\) \(xor\) \(a[j]\) (\(l<=i<=j<=r\)) 的最小值
解
先假设一个问题: 求 \(a[i]\) \(xor\) \(a[j]\) 的最大值
我们用分块+可持久化 \(trie\) 可以解决:
定义数组 \(f[i][j]\) 表示 \(l[i]\)~\(j\) 的上式最大值, 这里可以用可持久化 \(trie\) 求出, 即 \(query(l[i],root[y],a[j])\) 与 \(f[i][j-1]\) 取 \(max\), 可以理解为在 \(l[i]\)~\(j-1\) 的范围内每两个数字都考虑了 \(xor\), 扩展的点需要和区间所有的数考虑一次使得 \(l[i]\)~\(j\) 中每两个数字都考虑过了 \(xor\), 后得出最大值.
于是先预处理每一个 \(1<=i<=c\) 的 \(f[i][l[i]\)~\(n]\), 查询的时候找到大于等于 \(x\) 的最近的块起始界(当然 \(x.y\) 位于一个块内时需要额外考虑), 于是可以直接查询到 \(l[u]\)~\(y\) 中的所有数经过 \(xor\) 考虑后得出的最大值, 若要扩展到 \(x\)~\(y\), 则只需依次考虑下标为 \(x\)~\(l[u]-1\) 的数与区间每一个数的 \(xor\) 即可. 由于 \(xor\) 满足交换律, 故相反的所有位于 \(l[u]\)~\(y\) 的数同时也都考虑了与前面零碎的数的 \(xor\).
在此期间, 考虑 \(xor\) 用可持久化 \(trie\), 无论区间长短, 查询复杂度只有 \(O(log\) \(INF\)\()\)
对于此题, 求前缀 \(xor\) 和就可以将问题化为以上问题. 但是化为前缀和后, 备选域需要多考虑一下, 因为要得到 \(l\)~\(r\) 是用 \(s[r]-s[l-1]\)
exp
- 可以看见在此处左边界是固定的几个, 而右边界是可以随意查到的. 也就是在查询时只用考虑左边的零碎部分, 本题可以有这个方便是因为本题的零碎部分信息的朴素获取不需要依据区间内统计的数据. 举个例子, 众数.奇偶性的判断需要依赖于块内统计的该数的个数, 这需要空间来维护, 但是本题的 \(xor\) 结果不需要, 在 \(trie\) 的预处理之后就可以直接查询. 故众数等类型的分块题需要考虑两边零碎(当然若用二分下标求个数也可以只考虑一边, 但显然复杂度会多一个 \(log\)).
- 可持久化 \(trie\) 有一个数组叫 \(latest\), 非递归就可以维护, 不需要取 \(max\), 因为一般都是顺序添加新时间点, 每一个节点的 \(latest\) 放本时间点即可. 但是十分重要的一点(尤其对于涉及 \(l=0\) 的情形): latest[0]=-1. 因为当一个节点不存在时其 \(latest\) 为 \(0\), 当 \(l\) 为 \(0\) 就会误判此点存在, 当然多判一个 \(trie[p]\) 也可以避免.
- 块数分为 \(sqrt(m)\) 并非谬误, 均衡考虑一下预处理和查询的时间复杂度即可得到.
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ifor(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
const int N=1e4+2e3+5;
int n,m,tot,a[N],p[N],f[100][N],l[100],r[100],
root[N],trie[N*50][2],latest[N*50];
inline void insert(int p,int q,int val,int id)
{
for(int k=30;k>=-1;k--)
{
latest[q]=id;
if(k<0)
break;
int c=(val>>k)&1;
trie[q][c^1]=trie[p][c^1];
trie[q][c]=++tot;
p=trie[p][c],q=trie[q][c];
}
}
inline int query1(int l,int q,int val)
{
int res=0;
for(int k=30;k>=0;k--)
{
int c=(val>>k)&1;
if(latest[trie[q][c^1]]>=l)
res|=(1<<k),q=trie[q][c^1];
else q=trie[q][c];
}
return res;
}
inline int query2(int x,int y)
{
int u=p[x],v=p[y];
if(u==v)
{
int ans=0;
ifor(i,x,y)
ans=max(ans,query1(x-1,root[y],a[i]));
return ans;
}
++u;
int ans=f[u][y];
ifor(i,x-1,l[u]-2)
ans=max(ans,query1(x-1,root[y],a[i]));
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
ifor(i,1,n)
scanf("%d",&a[i]),a[i]^=a[i-1];//!!
int c=sqrt((double)m);
c=c>n?n:c;
int len=n/c;
ifor(i,1,c)
l[i]=(i-1)*len+1,r[i]=i*len;
if(r[c]<n)
l[c+1]=r[c]+1,r[++c]=n;
ifor(i,1,c)
ifor(j,l[i],r[i])
p[j]=i;
root[0]=++tot;
latest[0]=-1;
insert(0,root[0],0,0);
ifor(i,1,n)
root[i]=++tot,
insert(root[i-1],root[i],a[i],i);
ifor(i,1,c)
ifor(j,l[i],n)//!!
f[i][j]=max(f[i][j-1],query1(l[i]-1,root[j-1],a[j]));
long long L,R,pre=0;
ifor(i,1,m)
{
scanf("%lld%lld",&L,&R);
L=(L+pre)%n+1,R=(R+pre)%n+1;
if(L>R)
swap(L,R);
printf("%d\n",pre=query2(L,R));
}
}