bzoj 2741: 【FOTILE模拟赛】L (分块+可持久化trie树)

2741: 【FOTILE模拟赛】L

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 2572   Solved: 735
[ 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

[ Submit][ Status][ Discuss]

题解:分块+可持久化trie树

对于每个点维护1到i的异或前缀和,那么我们查询一段区间的异或和就转换成了求解两个点异或的最大值。

对于序列进行分块,用f[i][j]表示区间的左右端点在块i,j之间的两点异或的最大值。

对于序列建可持久化trie树

f[i][j]=max(f[i][j-1],右端点在块j左端点在该点到i的起始位置的两点异或最大值)

对于每个点可以利用trie树O(logn)的查询。

然后询问的时候直接利用f数组,然后半块的部分进行暴力。

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<cstring>  
#include<cmath>  
#define N 120  
#define M 20000  
#define bi 30  
#define LL long long  
using namespace std;  
int f[N][N],b[M],a[M];  
int sz,root[M],n,m,ch[M*60][3],size[M*60],l[M],r[M],belong[M];  
void insert(int i,int x)  
{  
    int pre=root[i-1];  
    root[i]=++sz;  
    int now=root[i];  
    for (int i=bi;i>=0;i--) {  
        LL t=(x>>i)&1;  
        size[now]=size[pre]+1;  
        ch[now][t^1]=ch[pre][t^1];  
        ch[now][t]=++sz;  
        now=ch[now][t]; pre=ch[pre][t];  
    }  
    size[now]=size[pre]+1;  
}  
int find(int x,int i,int j)  
{ 
    int a=root[i]; int b=root[j]; int ans=0;  
    for (int i=bi;i>=0;i--) {  
        LL t=(x>>i)&1;  
        if (size[ch[a][t^1]]!=size[ch[b][t^1]])   
         t^=1;  
        a=ch[a][t]; b=ch[b][t];  
        ans+=(1<<i)*t;  
    }  
    return ans;  
}  
int build(int now)  
{  
    int ans=0;  
    for (int i=l[now];i<=r[now]-1;i++)  
     for (int j=i+1;j<=r[now];j++)  
      ans=max(ans,b[j]^b[i-1]),ans=max(ans,a[i]);
    ans=max(ans,a[r[now]]);
    if (l[now]==r[now]) return a[l[now]];  
    return ans;  
}  
int main()  
{ 
    freopen("a.in","r",stdin);
	freopen("my.out","w",stdout); 
    scanf("%d%d",&n,&m);  
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);  
    for (int i=1;i<=n;i++) b[i]=b[i-1]^a[i];
    memset(root,0,sizeof(root));
    insert(1,0LL);  
    for (int i=1;i<=n;i++)   
     insert(i+1,b[i]);  
    int block=sqrt(n*1.0);  
    int t=ceil(n*1.0/block);  
    for (int i=1;i<=n;i++) belong[i]=(i-1)/block+1;  
    for (int i=1;i<=t;i++) l[i]=n+1,r[i]=0;  
    for (int i=1;i<=n;i++) {  
        int t=belong[i];  
        l[t]=min(l[t],i);  
        r[t]=max(r[t],i);  
    }  
    for (int i=1;i<=t;i++) f[i][i]=build(i);  
    for (int i=1;i<=t-1;i++)  
     for (int j=i+1;j<=t;j++){  
      f[i][j]=f[i][j-1];  
      for (int k=l[j];k<=r[j];k++)  
       f[i][j]=max(f[i][j],find(b[k],l[i]-1,k)^b[k]),f[i][j]=max(f[i][j],a[k]);
    }  
    int ans=0;  
    for (int j=1;j<=m;j++) {  
        int x,y; scanf("%d%d",&x,&y);  
        x=((LL)x+ans)%n+1; y=((LL)y+ans)%n+1;  
        if (x>y) swap(x,y);   
        ans=0;  
        if (belong[x]==belong[y]) {  
            for (int i=x;i<=y;i++)  
             ans=max(ans,find(b[i],(int)x-1,i)^b[i]);  
            printf("%d\n",ans);  
            continue;  
        }  
        if (belong[x]+1<=belong[y]-1) ans=f[belong[x]+1][belong[y]-1];  
        for (int i=x;i<=r[belong[x]];i++)  
         ans=max(ans,find(b[i-1],i,(int)y+1)^b[i-1]);  
        for (int i=l[belong[y]];i<=y;i++)  
         ans=max(ans,find(b[i],(int)x,i+1)^b[i]);  
        printf("%d\n",ans);  
    }  
}  



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值