BZOJ3261: 最大异或和

BZOJ3261: 最大异或和

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行,每行一个整数表示询问的答案。

Sample Input

5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
对于测试点 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。

Sample Output

4
5
6

题解Here!
本来以为是一道线段树题。。。
但是这个异或不怎么好维护。
看完题解发现是一道可持久化$Trie$树。。。
蒟蒻表示啥都不知道。。。

可持久化字典树就是记录在字典树上有相同前缀的前缀和(节点的个数)。

然后通过取差值(右边界减去左边界)判断一段区间内是否有字典树上的前缀。

做法就是对于每一个节点新建一颗字典树,记录下节点对应的字典树根的位置。

在插入过程中,对于插入的数的所有前缀都新建一个节点。

此节点拥有字典树上相同前缀的前缀和就是他父亲(此题是序列中的前一个数)的前缀和加一。

其他的前缀全部指向父亲的对应节点,因为前缀和没有改变。

然后此题

询问区间中一个点到$n$的异或和与$x$异或最大,也就是$n$的异或前缀与$x$异或后的值与区间内任意前缀亦或最大。

所以将$n$的异或前缀与$x$异或的值丢进字典树中贪心查找最大异或就可以了。

具体看代码吧,本蒟蒻也是第一次写这玩意,码风还没有成型。。。

附带码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 600010
using namespace std;
int n,m,size=0,sum=0;
int val[MAXN],root[MAXN];
struct Persistable_Trie{
    int val,son[2];
}a[MAXN<<5];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
void insert(int v,int rt,int f){
    root[rt]=++size;
    rt=root[rt];f=root[f];
    for(int i=28;i>=0;i--){
        int t=(v>>i)&1;
        if(!a[rt].son[t]){
            a[rt].son[t]=++size;
            a[rt].son[t^1]=a[f].son[t^1];
            a[a[rt].son[t]].val=a[a[f].son[t]].val;
        }
        rt=a[rt].son[t];
        f=a[f].son[t];
        a[rt].val++;
    }
}
int query(int l,int r,int v){
    int ans=0,s=val[l]^v;
    l=root[l];r=root[r];
    for(int i=28;i>=0;i--){
        int t=(v>>i)&1;
        if(a[a[r].son[t^1]].val>a[a[l].son[t^1]].val){
            ans+=1<<i;
            t^=1;
        }
        l=a[l].son[t];
        r=a[r].son[t];
    }
    return max(ans,s);
}
void work(){
    char ch[2];
    int x,y,k;
    while(m--){
        scanf("%s",ch);x=read();
        if(ch[0]=='A'){
            val[++n]=sum^=x;
            insert(sum,n,n-1);
        }
        else{
            y=read();k=read();
            printf("%d\n",query(x-1,y-1,sum^k));
        }
    }
}
void init(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
        val[i]=sum^=read();
        insert(sum,i,i-1);
    }
}
int main(){
    init();
    work();
    return 0;
}

 

posted @ 2018-08-12 23:45  符拉迪沃斯托克  阅读(226)  评论(0编辑  收藏  举报
Live2D