【题目描述】
Description
给定一个非负整数序列 {a},初始长度为 N。
有 M个操作,有以下两种操作类型:
1 、A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+1。
2 、Q l r x:询问操作,你需要找到一个位置 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
Sample Output
4
5
6
HINT
对于 100% 的数据, 0<=a[i]<=10^7 。
【题解】
首先要知道如何求一个数列的最大区间异或和。
令f[i]=a[1] xor a[2] xor …… xor a[i],那么区间[l,r]的异或值就是f[r] xor f[l-1]。
于是我们把f[i]转化为二进制插入线段树,那么枚举r,可以在trie树上贪心地寻找l。
那么这道题只要把f[i]插入可持久化线段树就可以了。
【Codes】
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int M=300010; int n,m,sum,i,l,r,x,cnt; int a[M*10],root[M*10]; struct node{int l,r,w;}tree[M*50]; void addtrie(int &p,int x,int dep){ tree[++cnt]=tree[p],p=cnt,tree[p].w++; if (dep<0)return; if (x&(1<<dep))addtrie(tree[p].r,x,dep-1);else addtrie(tree[p].l,x,dep-1); } int query(int l,int r,int x){ int ans=0,p=root[l-1],q=root[r]; for (int i=25;i>=0;i--) if (x&(1<<i)){ if (tree[tree[q].l].w-tree[tree[p].l].w)p=tree[p].l,q=tree[q].l,ans+=1<<i; else p=tree[p].r,q=tree[q].r; }else{ if (tree[tree[q].r].w-tree[tree[p].r].w)p=tree[p].r,q=tree[q].r,ans+=1<<i; else p=tree[p].l,q=tree[q].l; } return ans; } int main(){ scanf("%d%d",&n,&m); n++; int sum=0; addtrie(root[1],sum,25); for (i=2;i<=n;i++){ scanf("%d",&a[i]); sum^=a[i]; root[i]=root[i-1]; addtrie(root[i],sum,25); } for (i=1;i<=m;i++){ char opt[10]; scanf("%s",opt); if (opt[0]=='A'){ scanf("%d",&x); a[++n]=x; sum^=a[n]; root[n]=root[n-1]; addtrie(root[n],sum,25); }else{ scanf("%d%d%d",&l,&r,&x); printf("%d\n",query(l,r,sum^x)); } } }