给定序列, 若干个询问, 给定 \(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]\)
#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));
}
}
CH 4913 Fotile模拟赛L (可持久化trie std)
原文:https://www.cnblogs.com/cs-fil/p/14770020.html