bzoj3261 最大異或和 可持久化trie
阿新 • • 發佈:2018-11-25
連結
https://www.lydsy.com/JudgeOnline/problem.php?id=3261
做法
我們先考慮沒有修改操作,只有詢問的話怎麼做,我們記sum[i]表示i的字尾異或和,那麼我們每個詢問相當於查詢區間對一個數x異或後的最大值,那麼貪心很明顯,位數從高到低,如果有數這個位置上可以和x異或起來是1,那麼我肯定不會去選和x異或起來這位上是0的。根據這個性質,我們考慮trie樹,如果當前節點的子節點有能和x異或起來為1的節點,就走下去,不然只能走另一邊了,那麼區間的話我們就可持久化一下,這個具體就和主席樹是一模一樣的了,這裡不多贅述了。
接下來我們要考慮修改的問題,我們另sum[i]表示i的字首異或和,注意這裡和上面不同了,變成了字首,那麼我們每個點到n的異或和就可以表示為sum[i-1]^sum[n]了對吧,接下來的步驟就和上面是一樣的了。
程式碼
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<iostream>
#include<cmath>
#define LL long long
#define N (600005)
using namespace std;
int n,m,x,l,r,now,ans,cnt;
int sum[N],root[N],size[20000005],son[20000005][2];
char c[10];
template <typename T> void read(T&t) {
t=0;
bool fl=true;
char p=getchar();
while (!isdigit(p)) {
if (p=='-') fl=false;
p=getchar();
}
do {
(t*=10)+=p-48;p=getchar();
}while (isdigit(p));
if (!fl) t=-t;
}
void insert(int &u,int pre,int data,int ws){
u=++cnt;
size[u]=size[pre]+1 ;
//printf("%d %d %d\n",data,ws,size[u]);
if (ws==1) return;
son[u][0]=son[pre][0];
son[u][1]=son[pre][1];
if (data&(1<<ws-2)) insert(son[u][1],son[pre][1],data,ws-1);
else insert(son[u][0],son[pre][0],data,ws-1);
}
void query(int l,int r,int ws){
//printf("%d %d\n",ws,ans);
if (ws==1) return;
int cc=(now&(1<<ws-2));
if (cc) cc=0;
else cc=1;
//printf("ws %d %d\n",ws,cc);
if (size[son[r][cc]]-size[son[l][cc]]) ans+=(1<<(ws-2)),query(son[l][cc],son[r][cc],ws-1);
else query(son[l][cc^1],son[r][cc^1],ws-1);
}
int main(){
read(n),read(m);
insert(root[0],0,0,31);
for (int i=1;i<=n;i++){
read(x);
sum[i]=sum[i-1]^x;
insert(root[i],root[i-1],sum[i],31);
}
while (m--){
scanf("%s",c);
if (c[0]=='A'){
read(x);
n++;
sum[n]=sum[n-1]^x;
insert(root[n],root[n-1],sum[n],31);
}
if (c[0]=='Q'){
read(l),read(r),read(x);
now=sum[n]^x;
//printf("now %d\n",now);
ans=0;
int tt;
if (l==1) tt=0;
else tt=root[l-2];
query(tt,root[r-1],31);
printf("%d\n",ans);
}
}
return 0;
}