BZOJ 2741 分块+可持久化trie 解题报告

2741: 【FOTILE模拟赛】L

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

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

【解题报告】
分块+可持久化Trie

代码如下:

/**************************************************************
    Problem: 2741
    User: onepointo
    Language: C++
    Result: Accepted
    Time:8108 ms
    Memory:13588 kb
****************************************************************/

#include<cmath>  
#include<cstdio>  
#include<cstring>  
#include<iostream>  
#include<algorithm>  
#define M 13000  
#define ls son[0]  
#define rs son[1]  
using namespace std;  
struct Trie{  
    Trie *son[2];  
    int cnt;  
}*trie[M],**tree=trie+1,mempool[M*40],*C=mempool;  
int n,m,block,ans,a[M],s[120][M];  

inline Trie* New_Node(Trie*_,Trie*__,int ___)  
{  
    C->son[0]=_;  
    C->son[1]=__;  
    C->cnt=___;  
    return C++;  
}  
Trie* Build_Tree(Trie *p,int x,int pos)  
{  
    if(!pos) return New_Node(0x0,0x0,p->cnt+1);  
    if(~x&pos) return New_Node(Build_Tree(p->ls,x,pos>>1),p->rs,p->cnt+1);  
    else return New_Node(p->ls,Build_Tree(p->rs,x,pos>>1),p->cnt+1);  
}  
int Get_Ans(Trie *l,Trie *r,int x,int pos)  
{  
    int num=x&pos?1:0;    
    if(!pos) return 0;    
    if(r->son[!num]->cnt-l->son[!num]->cnt) return pos + Get_Ans(l->son[!num],r->son[!num],x,pos>>1);    
    else return Get_Ans(l->son[num],r->son[num],x,pos>>1);    
}  
int main()  
{  
    int i,j,x,y;  
    cin>>n>>m;  
    tree[-1]=New_Node(C,C,0);  
    tree[0]=Build_Tree(tree[-1],0,1<<30);  
    for(i=1;i<=n;i++)  
        scanf("%d",&a[i]),tree[i]=Build_Tree(tree[i-1],a[i]^=a[i-1],1<<30);  
    block=static_cast<int>(sqrt(n+1));  
    for(i=0;i*block<=n;i++)  
    for(j=i*block;j<=n;j++)  
        s[i][j]=max(Get_Ans(tree[i*block-1],tree[j],a[j],1<<30),j?s[i][j-1]:0);  
    for(i=1;i<=m;i++)  
    {  
        if(i==16) ++i,--i;  
        scanf("%d%d",&x,&y);  
        x=((long long)x+ans)%n+1;y=((long long)y+ans)%n+1;  
        if(x>y) swap(x,y); --x;  
        int temp=x/block+1;  
        ans=s[temp][y];  
        for(temp=min(temp*block-1,y);temp>=x;--temp)  
            ans=max(Get_Ans(tree[x-1],tree[y],a[temp],1<<30),ans);  
        printf("%d\n",ans);  
    }  
    return 0; 
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值