BZOJ3261 最大异或和

题目链接:戳我

可持久化01trie~~

其实还是先求出来前缀异或和,剩下的就是可持久化01trie——其实和主席树差不多,我们对每一个节点都建立一颗trie树。在这个题里面,用cnt[i]来表示节点编号为i的这个点所表示的前缀出现了多少次,我们利用差分思想就可以求出来这个前缀在当前区间中到底有没有出现。

注意我们建树的时候,假设当前这个节点没有'0'儿子,那我们就直接复制它的父亲的'0'儿子编号即可,(其实应该也可以赋予新的编号,然后让它和父亲的那个子节点相同。。。。不过这样的话更节省空间)然后因为它拥有'1'儿子,所以我们还是要给这个新子节点赋予一个编号,然后cnt++就可以了。

备注:查询的时候l--,r--的原因是,由于异或的性质,所以我们只需要求p-1,所以我们的区间就更改为了l-1,r-1。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1000010
using namespace std;
int n,m,tot;
int a[MAXN],rt[MAXN],ch[MAXN*25][2],cnt[MAXN*25];
inline void insert(int x,int f,int sum)
{
    for(int i=24;i>=0;i--)
    {
        int v=((sum>>i)&1);
        ch[x][!v]=ch[f][!v];
        ch[x][v]=++tot;
        cnt[ch[x][v]]=cnt[ch[f][v]]+1;
        x=ch[x][v],f=ch[f][v];
    }
}
inline int query(int x,int f,int sum)
{
    int cur_ans=0;
    for(int i=24;i>=0;i--)
    {
        int v=((sum>>i)&1);
        if(cnt[ch[x][!v]]>cnt[ch[f][!v]])
        {
            cur_ans+=(1<<i);
            x=ch[x][!v],f=ch[f][!v];
        }
        else x=ch[x][v],f=ch[f][v];
    }
    return cur_ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) a[i]^=a[i-1];
    rt[0]=++tot;
    insert(rt[0],0,0);
    //for(int i=1;i<=n;i++) printf("a[%d]=%d\n",i,a[i]);
    for(int i=1;i<=n;i++) rt[i]=++tot,insert(rt[i],rt[i-1],a[i]);
    for(int i=1;i<=m;i++)
    {
        char scur[3];
        int l,r,x;
        scanf("%s",scur);
        if(scur[0]=='A')
        {
            scanf("%d",&x);
            rt[++n]=++tot;
            a[n]=a[n-1]^x;
            insert(rt[n],rt[n-1],a[n]);
        }
        else
        {
            scanf("%d%d%d",&l,&r,&x);
            r--,l--;
            if(l==0) printf("%d\n",query(rt[r],0,x^a[n]));
            else printf("%d\n",query(rt[r],rt[l-1],x^a[n]));
        }
    }
    return 0;
}
posted @ 2019-03-06 08:16  风浔凌  阅读(111)  评论(0编辑  收藏  举报