[BZOJ 2741]【FOTILE模拟赛】L 分块+可持久trie树

236 篇文章 0 订阅
11 篇文章 0 订阅

首先很显然维护异或前缀和让询问区间连续一段变成询问两个单点.

然后就比较套路了,发现每一次都是询问一个区间内而且不满足我们已知知识内的一些特殊性质例如单调性,但是暴力又可以很容易的做出来,所以这个时候就可以考虑分块了设f[i][j] 表示第i块开始到j的最大异或值,然后就很简单了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define LL long long
#define maxn 20021
using namespace std;
int f[400][maxn],a[maxn],sum[maxn*40],ch[maxn*40][2],rt[maxn];
int tot,n,m,block,bl[maxn],num;
void insert(int x,int& y,int val){
	int root;root=y=++tot,sum[y]=sum[x]+1;
	for(int c,i=30;i>=0;i--){
		c=(val>>i)&1;
		ch[root][!c]=ch[x][!c];
		root=ch[root][c]=++tot;
		x=ch[x][c];
		sum[root]=sum[x]+1;
	}
}
int query(int a,int b,int val){
	a=rt[a-1],b=rt[b];
	int ans=0;
	for(int c,i=30;i>=0;i--){
		c=(val>>i)&1;
		if(sum[ch[b][!c]]-sum[ch[a][!c]])
			ans=ans<<1|1,b=ch[b][!c],a=ch[a][!c];
		else ans=ans<<1,a=ch[a][c],b=ch[b][c];
	}return ans;
}
void solve(){
	LL l,r,x,y;int ans=0;
	while(m--){
		scanf("%lld%lld",&x,&y);
		l=(x+ans)%n+1,r=(y+ans)%n+1;
		if(l>r)swap(l,r);
		l--,ans=0;
		if(bl[l]==bl[r]){
			for(int i=l;i<=r;i++)ans=max(ans,query(l,r,a[i]));
			printf("%d\n",ans);
		}else{
			int t=bl[l]*block;
			ans=f[bl[l]+1][r];
			for(int i=l;i<=t;i++)ans=max(ans,query(l,r,a[i]));
			printf("%d\n",ans);
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);block=sqrt(n);
	num=n/block+(n%block!=0);
	insert(rt[0],rt[0],0);
	for(int i=1;i<=n;i++){
		scanf("%d",a+i),a[i]^=a[i-1];
		insert(rt[i-1],rt[i],a[i]);
		bl[i]=(i-1)/block+1;
	}
	for(int s,i=1;i<=num;i++){
		s=(i-1)*block+1;
		for(int j=s;j<=n;j++){
			f[i][j]=max(f[i][j-1],query(s,j,a[j]));
		}
	}
	solve();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值