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。
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
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; }