【BZOJ2741】【FOTILE模拟赛】L 可持久化字典树+分块

广告:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44496739");
}

题解:

首先我们处理出来 sum[0,n] 作为异或前缀和,然后答案就不再是 [l,r] 中间某段区间的异或和,而转化成求了 [l1,r] 中任意两点异或和的最大值。

然后我们分块处理出 fi,j 表示 [i,j1] 这段区间中任取一点和点 j 异或和的最大值,而用gi,j做个类似前缀和的操作,记录第 i 块的开头到点j这段区间中任意两点异或和最大值, gi,j 可以由 max(gi,j1,fi,j) 更新得到。当然这里其实没有必要多开一个 g 数组的,直接有序地在f数组上做就可以了。

但是 f 数组怎么得到呢?我们可以写一个可持久化字典树,然后O(log2int)查询一个数和一段区间中某数的最大/最小异或和。(这个查询对于写这道题的人应该很水了,不会的问我。)

分块后:
我们已经处理出了 g 数组,那么对于每次询问[l,r]我们都可以先找出 l 右边第一个块,然后这个块头到r的两点最大异或和已经在 g 数组中预处理出来了,l到这个块头的部分我们枚举每个点,然后用之前维护完了的可持久化字典树查询区间中异或最大值更新本次答案。

啦。很详细了。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 13000
#define LN 35
#define SN 115
#define T 31
#define ls son[x][0]
#define rs son[x][1]
using namespace std;

int n,m,sum[N];

int blocks,size,belong[N];
int from[N],to[N];

struct Functional_Trie
{
    int root[N],cnt;
    int son[N*LN][2],size[N*LN];

    bool a[LN];
    void add(int w,int id)
    {
        root[id]=++cnt,size[cnt]=1;
        int i,x=root[id],y=root[id-1];
        root[id]=x;
        for(i=0;i<=T;i++)a[i]=((w>>i)&1);
        for(i=T;i>=0;i--)
        {
            if(a[i])
            {
                ls=son[y][0],rs=++cnt;
                size[cnt]=size[son[y][1]]+1;
                x=rs,y=son[y][1];
            }
            else {
                rs=son[y][1],ls=++cnt;
                size[cnt]=size[son[y][0]]+1;
                x=ls,y=son[y][0];
            }
        }
    }
    int query(int last,int now,int w)
    {
        int i,x=root[now],y=root[last-1];
        for(i=0;i<=T;i++)a[i]=((w>>i)&1);
        int ans=0;
        for(i=T;i>=0;i--)
        {
            if(a[i])
            {
                if(size[ls]-size[son[y][0]])
                    x=ls,y=son[y][0],ans+=(1<<i);
                else x=rs,y=son[y][1];
            }
            else {
                if(size[rs]-size[son[y][1]])
                    x=rs,y=son[y][1],ans+=(1<<i);
                else x=ls,y=son[y][0];
            }
        }
        return ans;
    }
}trie;
int ans[SN][N];
int main()
{
    int i,j,k;
    int a,b,c;

    scanf("%d%d",&n,&m),n++;
    trie.add(0,1);
    for(i=2;i<=n;i++)
    {
        scanf("%d",&sum[i]);
        sum[i]^=sum[i-1];
        trie.add(sum[i],i);
    }
    size=sqrt(n);
    for(i=1;i<=n;i+=size)
    {
        from[++blocks]=i;
        for(j=0;j<size&&(i+j<=n);j++)
            belong[i+j]=blocks;
        to[blocks]=i+j-1;
    }
    for(i=1;i<=n;i++)
    {
        int k=belong[i];if(i==from[k])k--;
        for(j=1;j<=k;j++)
        {
            ans[j][i]=trie.query(from[j],i-1,sum[i]);
            ans[j][i]=max(ans[j][i],ans[j][i-1]);
        }
    }
    int l,r,lastans=0;
    while(m--)
    {
        scanf("%d%d",&a,&b);
        l=((long long)a+lastans)%(n-1)+1;
        r=((long long)b+lastans)%(n-1)+1;
        if(l>r)swap(l,r);r++;

        lastans=0,k=belong[l];
        if(l==from[k])lastans=ans[k][r];
        else if(k==belong[r])
        {
            for(i=l;i<r;i++)for(j=i+1;j<=r;j++)
                lastans=max(lastans,sum[i]^sum[j]);
        }
        else {
            k++;
            lastans=ans[k][r];
            for(i=l;i<min(from[k],r);i++)
                lastans=max(lastans,trie.query(i+1,r,sum[i]));
        }
        printf("%d\n",lastans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值