【BZOJ 2741】【FOTILE模拟赛】L

2741: 【FOTILE模拟赛】L

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 1286   Solved: 332
[ Submit][ Status]

Description

FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和。
即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=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 ).
其中lastans是上次询问的答案,一开始为0。

Input

第一行两个整数N和M。
第二行有N个正整数,其中第i个数为Ai,有多余空格。
后M行每行两个数x,y表示一对询问。

Output

 

共M行,第i行一个正整数表示第i个询问的结果。

Sample Input

3 3
1 4 3
0 1
0 1
4 3


Sample Output

5
7
7

HINT



HINT

N=12000,M=6000,x,y,Ai在signed longint范围内。

Source


分块+可持久化trie。


根据异或的性质,我们可以预处理出前缀异或和,使问题转换为从一个区间选两个数,使其异或值最大。


Ask(l,r,s):表示在区间l-r中,找到与已知的s异或值最大的那个数。


假设已经知道Ask(l,r,s)可以在O(log(Max))时间内完成(Max是序列中的最大数),那么接下来就可以用分块来完成。


把序列分成sqrt(m)块,预处理b[i][j]表示第i块(块从0开始编号)的最左端到j(j<=n)中选两个数的最大异或值:


b[i][j]=max(b[i][j-1],Ask(i*sqrt(l),j-1,a[j]))     时间复杂度为O(sqrt(m)*n*log(Max))


然后对于每一个询问,找到在l-r区间中第一个出现的块的左端点为p,那么p-r中两个数最大的异或值已知了,只要求出l-p-1中的每一个数在l-r区间中的最大异或值即可,即Ask(l,r,a[i])  (l<=i<p)


那么Ask(l,r,s)怎么在O(log(Max))时间内完成?



用可持久化trie:


建立可持久化trie,并对每一个结点维护一个last值,表示最后是第几个数用到这个点的。


为了使异或值最大,我们尽量走与当前值相反的点(异或值为1),如果last值小于l的话,就不能走。


#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define M 10005
using namespace std;
int t[M][2],last[M*32]={-1},rt[M],a[M],b[80][M];
int c,l,tot=0,n,m;
void Insert(int root,int now,int k)
{
	int p;
	rt[k]=p=++tot;
	last[p]=k;
	for (int i=30;i>=0;i--)
	{
		int j=(now>>i)&1;
		t[p][j^1]=t[root][j^1];
		t[p][j]=++tot;
		p=tot,root=t[root][j];
		last[p]=k;
	}
}
int Ask(int l,int r,int x)
{
	int p=rt[r],ans=0;
	for (int i=30;i>=0;i--)
	{
		int j=((x>>i)&1)^1;
		if (last[t[p][j]]>=l) ans|=1<<i;
		else j^=1;
		p=t[p][j];
	}
	return ans;
}
int main()
{
        scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i]^=a[i-1];
	}
	c=(int)sqrt(m);
	l=n/c+(n%c!=0);
	Insert(rt[0],a[0],0);
	for (int i=1;i<=n;i++)
		Insert(rt[i-1],a[i],i);
	for (int i=0;i<c;i++)
		for (int j=i*l+1;j<=n;j++)
			b[i][j]=max(b[i][j-1],Ask(i*l,j-1,a[j]));
	int ans=0;
	while (m--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		x=(x+ans%n)%n+1,y=(y+ans%n)%n+1;
		if (x>y) swap(x,y);
		x--;
		int p=x/l+(x%l!=0);
		ans=p*l<y?b[p][y]:0;
		p=min(y,p*l);
		for (int i=x;i<=p;i++)
			ans=max(ans,Ask(x,y,a[i]));
		printf("%d\n",ans);
	}
	return 0;
}



感悟:

1.RE是因为数组开小了


2.感觉可持久化数据结构都一个样啊,都是借用之前的结点,再新建logn个结点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值