BZOJ3261最大异或和【可持久化Trie】

Description

给定一个非负整数序列{a},初始长度为N。
有M个操作,有以下两种操作类型:
1、Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。
2、Qlrx:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
a[p] xor a[p+1] xor … xor a[N] xor x 最大,输出最大是多少。

Input

第一行包含两个整数 N ,M,含义如问题描述所示。
第二行包含 N个非负整数,表示初始的序列 A 。
接下来 M行,每行描述一个操作,格式如题面所述。

Output

假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。

对于测试点 1-2,N,M<=5 。
对于测试点 3-7,N,M<=80000 。
对于测试点 8-10,N,M<=300000 。
其中测试点 1, 3, 5, 7, 9保证没有修改操作。
0<=a[i]<=10^7。


题目分析

先计算出数列的异或前缀和

a[N]^x是定值
所以问题所求就是在前缀和的l到r区间内找到一个数使其与a[N]^x异或值最大

经典的可持久化化Trie
建立N个01trie
第i个trie插入得是前缀和数列中前1~i个数
同时在每个节点维护一个sum记录该节点被插入过几次

建树方式和主席树几乎无差
学过主席树的同学应该一眼就能看懂

对于l~r区间同主席树思路一样
就是用第r棵与第l-1棵相减

查询得话每次走与当前数值相反的路就好

需要注意的是因为插入的是前缀和
每次查询区间要左移一位
或者像我这样把原数列全部后移一位,并将1建为空树


#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=1e7;
int n,m;
int xr,tot;
int trie[maxn<<1],sum[maxn<<1],nxt[maxn<<1][2];

int update(int pre,int x,int cnt) 
{
    int rt=++tot; sum[rt] = sum[pre] + 1;
    if(cnt<0) return rt;
    int d=x>>cnt&1;//二进制下当前位
    nxt[rt][d^1]=nxt[pre][d^1];//不用修改,指针指回上一棵主席树
    nxt[rt][d]=update(nxt[pre][d],x,cnt-1);//要新建的结点
    return rt;
}

int query(int u,int v,int x,int cnt)
{
    if(cnt<0) return 0;
    int d=x>>cnt&1;
    int ss=sum[nxt[v][d^1]]-sum[nxt[u][d^1]];//先判断相反位是否可以走
    if(ss>0) return (1<<cnt)+query(nxt[u][d^1],nxt[v][d^1],x,cnt-1);
    else return query(nxt[u][d],nxt[v][d],x,cnt-1);
}

int main()
{
    n=read();m=read();

    trie[1]=update(trie[0],0,24); n++;//后移一位并多建一棵空树
    for(int i=2;i<=n;++i)
    {
        int x=read(); xr^=x;
        trie[i]=update(trie[i-1],xr,24);//建立可持久化Trie
        //最后一个参数是数值的二进制位数,视具体数据而定
    }

    while(m--)
    {
        char ss; scanf("%s",&ss);
        if(ss=='A')
        {
            int x=read(); xr^=x; n++;
            trie[n]=update(trie[n-1],xr,24);//和上面建树一样
        }
        else
        {
            int ll=read(),rr=read(),x=read();
            printf("%d\n",query(trie[ll-1],trie[rr],xr^x,24));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/80440776