您的位置:首页 > 其它

【BZOJ 2741】【FOTILE模拟赛】L

2015-01-19 19:08 197 查看

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

By seter

分块+可持久化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个结点
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: