bzoj2741【FOTILE模拟赛】L

2741: 【FOTILE模拟赛】L

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 2032   Solved: 559
[ Submit][ Status][ Discuss]

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

By seter




分块+可持久化Trie树

分块的思路很棒!!!

看到连续异或和,很容易想到预处理出异或的前缀和s[i],然后将区间[l,r]的异或和转化为s[l-1]^s[r]。于是每个询问就转化为在s[l-1]到s[r]之间找两个数,使其异或值最大。

我们先考虑问题的简化版:在区间内找一个数,使它与另一个已知数的异或值最大。这个问题可以用可持久化Trie树解决,只需要在Trie树上走相异的节点。

那这个问题要如何解决呢?

我们先把区间分块,再预处理出f[i][j],表示从第i块的起始点到第j个点之间两个数异或的最大值。这可以采用递推:f[i][j]=max(f[i-1][j-1],query((i-1)*block+1,j,a[j]))。

然后对于每个询问,找到区间内第一个完整块的起始点x,答案赋值为f[pos[x]][r],然后对于左边剩余的点暴力计算并和答案取max。




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 20000
#define maxm 800000
using namespace std;
int n,m,tot,block,num;
int a[maxn],rt[maxn],pos[maxn],id[maxm],t[maxm][2],f[400][maxn];
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)
{
	int now=rt[k]=++tot;id[tot]=k;
	pre=rt[pre];
	D(i,30,0)
	{
		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];
	D(i,30,0)
	{
		if (id[tmp]<l) break;
		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()
{
    n=read();m=read();
    block=int(sqrt(n));num=n/block+(n%block!=0);
    F(i,1,n) pos[i]=(i-1)/block+1;
    id[0]=-1;insert(0,0,0);
    F(i,1,n) a[i]=a[i-1]^read();
    F(i,1,n) insert(i-1,i,a[i]);
    F(i,1,num) F(j,(i-1)*block+1,n)
    	f[i][j]=max(f[i][j-1],query((i-1)*block+1,j,a[j]));
	int ans=0;
	while (m--)
	{
		ll l,r,x=read(),y=read();
		l=((ll)ans+x)%n+1;r=((ll)ans+y)%n+1;
		if (l>r) swap(l,r);
		ans=0;l--;
		if (pos[l]==pos[r])
		{
			F(i,l,r) ans=max(ans,query(l,r,a[i]));
		}
		else
		{
			ans=f[pos[l]+1][r];
			F(i,l,pos[l]*block) ans=max(ans,query(l,r,a[i]));
		}
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(分块,bzoj,可持久化Trie树)