[BZOJ]2741 [FOTILE模拟赛]L

本文介绍了一种解决特定算法问题的方法,该问题是求解给定序列中区间的最大连续异或和。通过使用前缀异或和的概念,并结合持久化trie树结构,实现了高效的查询与更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2741: 【FOTILE模拟赛】L

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 2939   Solved: 850
[ 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]


HOME Back


这道题是裸的区间最大连续异或和,因为题目就让你求这个对吧...

考虑求前缀异或和,那么sum[L-1]^SUM[R]就是L到R得连续异或和,因为我们sum存储的是前缀,又因为相同的数异或为0,且sum[R]包含了sum[L-1],所以两者异或起来,1~L-1的异或贡献被抵消,就只剩下了L~R的贡献.那么问题就转化成为,在L-1到R内选择两个数的sum值异或起来最大.

考虑对于一个数 x 的sum,求与他之前的某个数异或起来最大,那么贪心的肯定让高位的1得到保证,由异或的性质可得我们要从高位到低位找到一个数的高位到低位的每一位尽量与x的每一位相反.那么我们对于序列上的每个点i就建立可持久化trie树维护1~i的sum值,trie树里面保存了每个数的二进制值,从高位到低位深度变深,这是一个01trie树.那么R的trie树-(L-1)trie的trie树就得到维护了L~R sum值 的01trie,那么对于这个区间里的每个数的sum就可以顺着这个01trie从高位到低位贪心选择与当前数的sum值的这一位不相同的,就可以找到最大值.但是这样对于每个询问是o(区间长度*trie树树高)的,那么我们就分块预处理处理每个块到每个点的答案(数据不大,所以可以空间允许精确到每个点),然后对于L到R包含的块的答案就能o(1)得到,不在块内的暴力查trie即可,可以结合代码理解.

#include<stdio.h>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=12005;
const int maxbit=31;
inline const int read(){
    register int f=1,x=0;
    register char ch=getchar(); 
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return f*x;
}
int a[maxn],c[maxn*1000][2],sum[maxn*1000],init[200][maxn],tr[maxn],st,n,m,block,blo[maxn],id,Whereever_I_Go,ed,_wanna,maxx,lastans;
struct Trie{
    void insert(int &rt,int pre,int who,int pos){
       rt=++id;
       c[rt][0]=c[pre][0];c[rt][1]=c[pre][1];
       sum[rt]=sum[pre]+1;
       if(pos==-1) return;
       Whereever_I_Go=(who>>pos)&1;
       insert(c[rt][Whereever_I_Go],c[pre][Whereever_I_Go],who,pos-1);
    }
    inline int query(int l,int r,int who){
       ed=0;
       for(int i=maxbit;i>=0;i--){
           _wanna=((who>>i)&1)^1;
           int p=c[l][_wanna],q=c[r][_wanna];
           if(sum[q]>sum[p]) ed|=(1<<i),l=p,r=q;
           else l=c[l][_wanna^1],r=c[r][_wanna^1];
       }
       return ed;
    }
}trie;
int main(){
    n=read(),m=read(),block=(int)sqrt(n);tr[0]=0;
    for(register int i=1;i<=n;i++) a[i]=read(),a[i]^=a[i-1];
    for(register int i=1;i<=n;i++) trie.insert(tr[i],tr[i-1],a[i],maxbit);
    for(register int i=1;i<=n;i++) blo[i]=(i-1)/block+1;
    maxx=n/block+(n%block);
    for(int i=1;i<=maxx;i++){
        st=(i-1)*block+1;
        for(int j=st;j<=n;j++){
            init[i][j]=max(init[i][j-1],a[j]^a[st-1]);
            init[i][j]=max(init[i][j],trie.query(tr[st-1],tr[j],a[j]));
        }
    }
    int l,r;
    while(m--){
        l=read(),r=read();
        l=(l+(long long)lastans)%n+1;
        r=(r+(long long)lastans)%n+1;
        if(l>r) swap(l,r);l--;
        lastans=(blo[r]>blo[l])?init[blo[l]+1][r]:0;
        int lim=min(blo[l]*block,r);
        for(int i=l;i<=lim;i++) lastans=max(lastans,trie.query(tr[l-1],tr[r],a[i]));
        printf("%d\n",lastans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值