【BZOJ 3261】最大异或和(可持久化trie树)

题目链接

题意

给出一个序列,有两种操作。
一种是在序列后面加数,一种是询问一个区间内的所有后缀和一个给定数的最大异或和。

Sol

每次动态询问一个后缀和一个数的异或最值。
我们显然可以把每一个后缀的异或和看成是一个数。

但是由于每次插入是从后插入,如果我们维护后缀的化那么就要改很多东西了。
于是只能维护前缀,但怎么算呢。容斥一下,用全局的和异或一下就好了。

然后就是区间的异或最值问题了。

于是就写一个可持久化的trie树。

怎么写呢?

做法和主席树类似。记录每一棵trie的根节点。
每次更新的时候,不用进行修改的子节点就直接继承上上一棵trie的子节点。要修改的就新开节点就可以了。

询问也类似。我们在每一个点维护子树内有多少个数(终止节点,相同的也要算),然后求 l r 某一个位为 0 / 1 的数的时候直接用 r t r i e c o u n t 减掉 l 1 t r i e c o u n t 就可以了。

需要注意的是,由于我们这里维护的是前缀,时间戳应该要后移一位,具体来说就是先插入数再修改当前全局异或和。

代码:

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<ctime>
#include<cstdio>
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
int n,m;
const int N=3e5+10;
namespace trie{
    int rt[N<<1];
    const int Up=24;int cnt=0;
    int next[2][50*N];
    int count[50*N];
    int Sum=0;
    void insert(int &now,int x){
        register int p1=now;now=++cnt;register int p2=cnt;
        for(register int i=Up;~i;--i){
            next[0][p2]=next[0][p1],next[1][p2]=next[1][p1],count[p2]=count[p1]+1;//继承
            register int k=(x>>i)&1;
            next[k][p2]=++cnt;p2=cnt;p1=next[k][p1];//新开
        }
        count[p2]=count[p1]+1;
    }
    void build(){
        for(register int i=1;i<=n;++i){
            register int x=read();
            rt[i]=rt[i-1];insert(rt[i],Sum);
            Sum^=x;
        }
        return;
    }
    inline int query(int l,int r,int x){
        register int p1=rt[l],p2=rt[r];
        register int ans=0;
        for(register int i=Up;~i;--i){
            ans<<=1;
            register int k=(x>>i)&1;
            register int sz=count[next[k^1][p2]]-count[next[k^1][p1]];
            if(sz) ans|=1,p1=next[k^1][p1],p2=next[k^1][p2];
            else p1=next[k][p1],p2=next[k][p2];
        }
        return ans;
    }
    void work(){
        register int x,l,r;count[0]=next[0][0]=next[1][0]=0;
        for(register int i=1;i<=m;++i){
            char ch=getchar();
            while(ch!='Q'&&ch!='A') ch=getchar();
            if(ch=='A'){x=read();++n;rt[n]=rt[n-1];insert(rt[n],Sum);Sum^=x;}
            else{
                l=read();r=read();x=read();x^=Sum;
                printf("%d\n",query(l-1,r,x));
            }
        }
    }
}
int main()
{
    n=read();m=read();
    trie::build();trie::work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/element_hero/article/details/81253620