异或(xor.c/cpp/pas)
2.1 题目描述
给出 n 个数,Q次询问,每次问[l,r]中最大连续异或和。
为了体现在线操作,对于每次询问(x,y):
l=min( ((x+lastans) mod n)+1 , ((y+lastans) mod n)+1 )
r=max( ((x+lastans) mod n)+1 , ((y+lastans) mod n)+1 )
2.2 输入格式
第一行为两个整数n,m,分别表示数的个数和询问次数。
接下来一行 n个数,再接下来 m行,每行两个数 x,y,表示给出询问(x,y),通过上述操作得到l和r,查询[l,r]中最大连续异或和。
2.3 输出格式
输出m行,每行一个整数表示该次询问的答案。
2.4 样例输入
3 3
1 4 3
0 1
0 1
4 3
2.5 样例输出
5
7
7
2.6 数据范围与约定
对于30%的数据,n<=500,Q<=500。
对于100%的数据,n<=12000 , Q<=6000 , 给出的数均在signed longint范围内,且均为非负数。
题解
一眼看出BZOJ2741原题。。。回滚莫队水过。
分块+可持久化Trie树贪心。为了方便大家看,将讲稿粘贴于此。
将 n 个数分成sqrt(n)个块。考虑用 w[i][j] 表示从第 i 个块开头元素到第 j 个元素这个区间中,最大连续异或和。建可持久化Trie树并且预处理出w数组。预处理复杂度为 O(n * sqrt(n) * 位数)。
查询[l,r]时,令 p 为 l 以右第一个块开头元素,那么首先可以直接得到 p 到 r 区间的答案。再考虑上 [l,p-1] 区间中的元素,逐个在可持久化Trie上贪心即可。查询总复杂度为O(Q * sqrt(n) * 位数)。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 20000
#define M 800000
using namespace std;
int n,m,tot,block,num;
int a[N],rt[N],pos[N],id[M],t[M][2],f[400][N];
inline int read(){
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void insert(int pre,int k,int x){
register int now=rt[k]=++tot;id[tot]=k;
pre=rt[pre];
for(register int i=30;i>=0;--i){
register int j=(x>>i)&1;
t[now][j^1]=t[pre][j^1];
t[now][j]=++tot;id[tot]=k;
now=t[now][j];pre=t[pre][j];
}
}
inline int query(int l,int r,int x){
int ans=0,tmp=rt[r];
for(register int i=30;i>=0;i--){
if(id[tmp]<l)break;
register int j=((x>>i)&1)^1;
if(id[t[tmp][j]]>=l)ans|=(1<<i);
else j^=1
tmp=t[tmp][j];
}
return ans;
}
int main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
n=read();m=read();
block=int(sqrt(n));num=n/block+(n%block!=0);
for(register int i=1;i<=n;++i)pos[i]=(i-1)/block+1;
id[0]=-1;insert(0,0,0);
for(register int i=1;i<=n;++i)a[i]=a[i-1]^read();
for(register int i=1;i<=n;i++)insert(i-1,i,a[i]);
for(register int i=1;i<=num;++i)
for(register int j=(i-1)*block+1;j<=n;++j)
f[i][j]=max(f[i][j-1],query((i-1)*block+1,j,a[j]));
register int ans=0;
while(m--){
ll l=read(),r=read();
l=((ll)ans+l)%n+1;
r=((ll)ans+r)%n+1;
if(l>r)swap(l,r);
ans=0;l--;
if(pos[l]==pos[r]){
for(register int i=l;i<=r;++i)
ans=max(ans,query(l,r,a[i]));
}else{
ans=f[pos[l]+1][r];
for(register int i=l;i<=pos[l]*block;++i)
ans=max(ans,query(l,r,a[i]));
}
printf("%d\n",ans);
}
return 0;
}
对于这道题来说,最大的看点在于如何处理好与平时分块不同的预处理方法。一般的分块处理的只是块内的信息,但是这中分块方法处理了块与块之间的联系。