1. 程式人生 > >bzoj3261 最大異或和 可持久化trie

bzoj3261 最大異或和 可持久化trie

連結

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