给定一个非负整数序列 {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 最大,输出最大是多少。
给定一个非负整数序列 {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 最大,输出最大是多少。
第一行包含两个整数 N ,M,含义如问题描述所示。
第二行包含 N个非负整数,表示初始的序列 A 。
接下来 M行,每行描述一个操作,格式如题面所述。
假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。
对于 100% 的数据, 0<=a[i]<=10^7 。
可持久化Trie树第一题。
首先我们可以维护前缀异或和(这里充分利用了异或的性质),然后就是求x^sum[n]^sum[p-1]的最大值。又因为x^sum[n]是定值,所以在Trie树上贪心即可。
考虑到p是在区间[l-1,r-1]内的,所以我们不能对于每次询问建一个Trie树。但是我们可以对于Trie树维护前缀和,建立可持久化Trie树。这样每次询问,只要在trie[r-1]-trie[l-1]上贪心就可以了。
还有两点需要注意的:
1.一开始要插入一个数0,因为最初异或和等于0。
2.对于每个点要维护一个id,这样便于询问操作中判断该节点是否在区间[l-1,r-1]内。特别地,id[0]=1,这保证了空节点不会被访问。(详见代码)
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 600005 #define maxm 20000005 using namespace std; int n,m,tot=0,sum=0; int rt[maxn],id[maxm],t[maxm][2]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void insert(int pre,int x,int k) { int now=rt[k]=++tot;id[tot]=k; D(i,24,0) { int j=(x>>i)&1; t[now][j^1]=t[pre][j^1]; t[now][j]=++tot;id[tot]=k; now=t[now][j];pre=t[pre][j]; } } inline int query(int l,int r,int x) { int ans=0,tmp=rt[r]; D(i,24,0) { if (id[tmp]<l) break; int j=((x>>i)&1)^1; if (id[t[tmp][j]]>=l) ans|=(1<<i); else j^=1; tmp=t[tmp][j]; } return ans; } int main() { n=read();m=read(); id[0]=-1; insert(0,0,0); F(i,1,n) { sum^=read(); insert(rt[i-1],sum,i); } F(i,1,m) { char ch=getchar(); while (ch<'A'||ch>'Z') ch=getchar(); if (ch=='A') { sum^=read(); insert(rt[n],sum,n+1); n++; } else { int l=read(),r=read(),x=read(); printf("%d\n",query(l-1,r-1,sum^x)); } } return 0; }