bzoj2741 FOTILE模拟赛L 可持久化Trie

       首先将作前缀和sum[i]=a[1]^a[2]^...^a[i],将询问变为max{sum[x-1]^sum[y]},l<=x<=y<=r。

       假设我们要得到max{sum[x]^sum[y]},l<=y<=r的值,我们可以构建一颗Trie,存储sum[l],sum[l+1],...sum[r]的二进制串,然后再Trie上面贪心找最大值(能走不同就走不同,可以见USACO TRAING的Xor一题)。

       显然不可能存储n^2个Trie,然后我们可以用和主席树一样的方法构建可持久化Trie。实际上,构建得到的可持久化Trie和sum[]数组的主席树是一模一样的!只是查询的时候有点不同而已。

       但是对于本题的询问,x是可以变化的。我们可以分块,然后用f[i][j]表示x>=第x块的头,y<=j时的max{sum[x-1]^sum[y]}(在代码中稍有不同)。那么对于查询(l,r),设l属于块u,那么首先得到ans=f[u+1][r],然后枚举块u中的可行节点更新答案即可。

       时间复杂度O((M+N)N^0.5logN)。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 13005
#define ll long long
using namespace std;

int n,m,trtot,ls[N<<5],rs[N<<5],sum[N<<5],rt[N];
int l[N],r[N],blg[N],f[115][N],a[N],bin[35];
void ins(int x,int &y,int k,int v){
	y=++trtot; sum[y]=sum[x]+1;
	if (k<0) return;
	if (bin[k]&v){ ls[y]=ls[x]; ins(rs[x],rs[y],k-1,v); }
	else{ rs[y]=rs[x]; ins(ls[x],ls[y],k-1,v); }
}
int qry(int x,int y,int z){
	x=rt[x-1]; y=rt[y]; int k=30,ans=0;
	for (; k>=0; k--){
		int tmp=z&bin[k];
		if (tmp)
			if (sum[ls[y]]-sum[ls[x]]){ x=ls[x]; y=ls[y]; ans|=bin[k]; }
			else{ x=rs[x]; y=rs[y]; }
		else if (sum[rs[y]]-sum[rs[x]]){ x=rs[x]; y=rs[y]; ans|=bin[k]; }
			else{ x=ls[x]; y=ls[y]; }
	}
	return ans;
}
int main(){
	scanf("%d%d",&n,&m); int i,j;
	bin[0]=1; for (i=1; i<=30; i++) bin[i]=bin[i-1]<<1;
	a[1]=0; ins(rt[0],rt[1],30,0); n++;
	for (i=2; i<=n; i++){
		scanf("%d",&a[i]); a[i]^=a[i-1];
		ins(rt[i-1],rt[i],30,a[i]);
	}
	int cnt=(int)sqrt((double)n);
	for (i=1; i<=cnt; i++){
		l[i]=r[i-1]+1; r[i]=r[i-1]+cnt;
	}
	r[cnt]=n;
	for (i=1; i<=cnt; i++){
		for (j=l[i]; j<=r[i]; j++) blg[j]=i;
		for (j=l[i]; j<=n; j++)
			f[i][j]=max(f[i][j-1],qry(l[i],j,a[j]));
	}
	int ans=0,x,y;
	for (i=1; i<=m; i++){
		scanf("%d%d",&x,&y);
		x=((ll)x+ans)%(n-1)+1; y=((ll)y+ans)%(n-1)+1;
		if (x>y) swap(x,y); y++;
		ans=(blg[x]<blg[y])?f[blg[x]+1][y]:0;
		for (j=x; j<=r[blg[x]] && j<=y; j++)
			ans=max(ans,qry(x,y,a[j]));
		printf("%d\n",ans); y--;
	}
	return 0;
}


by lych

2016.2.27

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值