[BZOJ]2741: 【FOTILE模拟赛】L 可持久化Trie+分块

8 篇文章 0 订阅

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。

题解:

今天终于知道XOR和可持久化Trie之间的联系了……太强了……
异或连续和Ai xor Ai+1 xor Ai+2 … xor Aj,可以先预处理A的异或前缀和,那么可以转化为 Si xor Sj-1。所以询问x,y可以转化为在Sx-1~Sy中找两个S,使它们的异或值最大。我们先考虑,对于一个固定的值x,如何找到一个数与它的异或值最大,用贪心的思想,在Trie树上尽量走与x相反的路线(也就是说如果x那一位上是1,就尽量走0,是0的话就尽量走1),就可以解决。又由于是在某个区间内找,所以要可持久化Trie。然后对于这道题,先用 O(nnlogn) 的时间预处理出 f[i][j] f[i][j] 表示第i块的起点到j的答案是多少,接着每次询问就可以在 O(nlogn) 的时间内解决。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int M=800000;
const int maxn=12010;
int n,m,a[maxn],bel[maxn],num,root[maxn],f[120][maxn];
//f[i][j]表示第i块的起始点到j的答案 
int id[M],son[M][2],tot=0;
//id[i]表示i这个节点属于哪个数
void ins(int last,int p,int x)
{
    root[p]=++tot;id[tot]=p;last=root[last];
    int now=tot;
    for(int i=30;i>=0;i--)
    {
        int t=((x>>i)&1);
        son[now][t^1]=son[last][t^1];//没有这个节点,直接等于上一个 
        son[now][t]=++tot;id[tot]=p;//新建节点
        now=son[now][t];last=son[last][t];
    }
}
int query(int l,int r,int x)
{
    int now=root[r],ans=0;
    for(int i=30;i>=0;i--)
    {
        if(id[now]<l)break;//不在范围内,退出 
        int t=((x>>i)&1);
        if(id[son[now][t^1]]>=l)ans|=(1<<i);//此节点在范围内,对答案有贡献 
        else t^=1;//这一位无贡献 
        now=son[now][t^1];
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    int o=(int)(sqrt(n));a[0]=bel[0]=bel[n]=0;
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),a[i]^=a[i-1],bel[i]=(i-1)/o+1;
    num=bel[n];id[0]=-1;ins(0,0,0);
    for(int i=1;i<=n;i++)ins(i-1,i,a[i]);
    for(int i=1;i<=num;i++)
    {
        int st=(i-1)*o+1;//第i块起点
        for(int j=st;j<=n;j++)
        f[i][j]=max(f[i][j-1],query(st,j,a[j]));
    }
    int ans=0;
    while(m--)
    {
        LL x,y;
        scanf("%lld%lld",&x,&y);
        x=((LL)ans+x)%n+1;y=((LL)ans+y)%n+1;
        if(x>y)swap(x,y);
        ans=0;
        if(bel[x]==bel[y])
        {
            for(int i=x-1;i<=y;i++)
            ans=max(ans,query(x-1,y,a[i]));
        }
        else
        {
            ans=f[bel[x-1]+1][y];
            for(int i=x-1;i<=bel[x-1]*o;i++)
            ans=max(ans,query(x-1,y,a[i]));
        }
        printf("%d\n",ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值