1. 程式人生 > >【xsy1098】第k小 可持久化trie

【xsy1098】第k小 可持久化trie

printf code 相同 異或操作 bsp use namespace print ret

題目大意:你要維護一個長度為$n$的序列,資瓷對整個序列$xor,and,or$一個數,以及區間第k小查詢。

數據範圍:$n≤50000$,所有數字$<2^{31}$。

此題甚妙

我們不難想出沒有位運算的區間第k大查詢,直接可持久化trie就可以了。

考慮此題只有xor操作。

我們記一個$last$表示之前所有異或操作的數的異或和。

查詢區間第$k$小時,隨便查詢下就行了(在比較時假裝交換$trie$的左右子樹)

考慮存在$or$和$and$操作,我們設對序列操作的數字為$x$,設$vis[k]$表示整個序列的第$k$位是否被全部歸零過。

不難發現,$and$操作對第i位有意義,當且僅當數字$x$的第$i$位為$0$,($or$情況類似等下會講),在這種情況下,對於序列上的所有數,第$i$位都會歸零,(last的第$i$位也會歸零)

同理,$or$操作對第i位有意義,當且僅當數字$x$的第$i$位為$1$,對於序列上所有數,第$i$位都會變為1($last$的第$i$位也會變為1)

實際上兩個操作本質上是相同的,我們可以用$and$操作和$last|=x$去實現$or$操作。

不難發現,對於數列的每一位,歸零操作至多只需要執行一次。

那麽,對於每個需要執行歸零操作的$or$或$and$操作,我們重建一次可持久化$trie$就可以了。

時間復雜度$O(n\ log^2 V+m\ log V) $其中V為數列中的最大值。

 1 #include<bits/stdc++.h>
 2 #define
M 50005 3 #define INF 2147483647 4 using namespace std; 5 6 struct trie{int a[2],siz;}a[M*33]={0}; int use=0; 7 void add(int &x,int dep,int zhi){ 8 a[++use]=a[x]; a[x=use].siz++; 9 if(dep==-1) return; 10 bool k=zhi&(1<<dep); 11 if(k) add(a[x].a[1],dep-1,zhi);
12 else add(a[x].a[0],dep-1,zhi); 13 } 14 int num[M]={0},n,m,zhi=0,root[M]={0},vis[32]={0},pls=INF; 15 16 int query(int x,int y,int dep,int K){ 17 if(dep==-1) return 0; 18 bool k=zhi&(1<<dep); 19 int now=a[a[y].a[k]].siz-a[a[x].a[k]].siz,res=1<<dep; 20 if(K<=now) res=query(a[x].a[k],a[y].a[k],dep-1,K); 21 else res+=query(a[x].a[k^1],a[y].a[k^1],dep-1,K-now); 22 return res; 23 } 24 25 void build(){ 26 for(int i=1;i<=n;i++) num[i]=num[i]&pls; 27 use=0; 28 for(int i=1;i<=n;i++){ 29 root[i]=root[i-1]; 30 add(root[i],30,num[i]); 31 } 32 } 33 int main(){ 34 scanf("%d%d",&n,&m); 35 for(int i=1;i<=n;i++) scanf("%d",num+i); 36 build(); 37 while(m--){ 38 char op[4]; int x,l,r,k; pls=INF; 39 scanf("%s",op); 40 if(op[0]==X){scanf("%d",&x); zhi^=x;} 41 if(op[0]==O){ 42 scanf("%d",&x); 43 for(int i=0;i<=30;i++) 44 if(((1<<i)&x)){ 45 if(vis[i]==0) vis[i]=1,pls^=(1<<i); 46 zhi|=(1<<i); 47 } 48 if(pls==INF) continue; 49 build(); 50 } 51 if(op[1]==n){ 52 scanf("%d",&x); 53 for(int i=0;i<=30;i++) 54 if(((1<<i)&x)==0){ 55 if(vis[i]==0) vis[i]=1,pls^=(1<<i); 56 zhi&=(INF-(1<<i)); 57 } 58 if(pls==INF) continue; 59 build(); 60 } 61 if(op[1]==s){ 62 scanf("%d%d%d",&l,&r,&k); 63 printf("%d\n",query(root[l-1],root[r],30,k)); 64 } 65 } 66 }

【xsy1098】第k小 可持久化trie