可持久化 $trie$ ....又是一個表裏不一的東西.....php
和主席樹相似的,其實可持久化就是體如今前綴信息的維護上(搞不懂這怎麼就叫作可持久化了...)html
$trie$ (字典樹)你們應該都知道,就是一棵用來作字符串匹配的樹,ios
可是!在這裏,可持久化 $trie$ 就是徹底不同的東西了...git
基本上(我作過的題),可持久化都是用來維護 $XOR$ 信息的...算法
好比說求某個範圍內的最大區間異或和之類的,至於到了樹上嘛,你懂的.ide
仍是和主席樹相似的,可持久化 $trie$ 就是要你在一棵樹上(因爲是異或,數字都會變成二進制,值只有 0 和 1 兩種表示,因而這棵樹天然就是二叉樹了)維護每一個前綴出現的次數(這裏就是相似 trie 的作法)優化
哎...相信你是沒有看懂的...因而邊看代碼邊本身感性理解一下吧.... spa
這實際上是一道板子題的代碼...code
大致思路就是和主席樹差很少,若是當前處理到了 0 ,那麼 當前節點的 1 的孩子直接調用 las 所指向的孩子 1 就行了,htm
而後當前節點 和 las 節點都跳向 0 這個孩子,而且處理的這個過程是從高位到低位的(以符合查詢時貪心的思想)
每次更新都是新增 30 (通常來講是這樣,具體得看題目的數據範圍) 個節點,因此不會炸
1 //by Judge 2 #include<iostream> 3 #include<cstdio> 4 using namespace std; 5 const int M=3e7+111; 6 inline int read(){ 7 int x=0,f=1; char c=getchar(); 8 for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 9 for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 10 } 11 inline int cread(){ 12 char c=getchar(); while(c!='Q' && c!='A') c=getchar(); return c^'Q'; 13 } 14 int n,m,cnt; 15 int rt[M],son[M][2],d[30],sum[M]; 16 inline void split(int k){ 17 int i,len=0; 18 while(k) d[++len]=k&1,k>>=1; 19 for(int i=len+1;i<=27;++i) d[i]=0; 20 } 21 inline void update(int& now,int las){ 22 sum[now=++cnt]=sum[las]+1; 23 int i,tmp=now; 24 for(i=27;i;--i){ 25 son[tmp][d[i]^1]=son[las][d[i]^1], 26 son[tmp][d[i]]=++cnt,las=son[las][d[i]], 27 sum[tmp=cnt]=sum[las]+1; 28 } 29 } 30 inline int query(int u,int v){ 31 int ans=0,i; 32 for(i=27;i;--i){ 33 if(sum[son[v][d[i]^1]]-sum[son[u][d[i]^1]]>0) 34 ans|=(1<<i-1),u=son[u][d[i]^1],v=son[v][d[i]^1]; 35 else u=son[u][d[i]],v=son[v][d[i]]; 36 } return ans; 37 } 38 int main(){ 39 int sum=0,x,opt,l,r; 40 n=read(),m=read(),++n; 41 split(0),update(rt[1],rt[0]); 42 for(int i=2;i<=n;++i) 43 split(sum^=x=read()), 44 update(rt[i],rt[i-1]); 45 for(int i=1;i<=m;++i){ 46 opt=cread(); 47 if(opt) 48 split(sum^=x=read()), 49 update(rt[n+1],rt[n]),++n; 50 else 51 l=read(),r=read(),x=read(),split(x^sum), 52 printf("%d\n",query(rt[l-1],rt[r])); 53 } return 0; 54 }
其實上面已是一道了。
而後這道(樹上搞事情)的題:Tree
其實樹上 可持久化 trie 和樹上主席樹相似,就是當前節點調用的 las 節點變成了該節點的父節點,查詢的時候也是和樹上主席樹相似的套路,
這裏和樹上主席樹同樣是要查詢 LCA 的,咱們用樹剖維護便可(並且還能夠在樹剖時維護每一個節點的可持久化信息)
1 //by Judge 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 const int M=1e5+111; 7 inline int read(){ 8 int x=0,f=1; char c=getchar(); 9 for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 10 for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 11 } 12 int n,m,pat,cnt; 13 int head[M],d[20],rt[M],to[M<<5][2],sum[M<<5]; 14 int val[M],siz[M],dep[M],top[M],f[M],son[M]; 15 struct Edge{ 16 int to,next; 17 Edge(int to,int next): to(to),next(next){} Edge(){} 18 }e[M<<1]; 19 inline void add(int u,int v){ 20 e[++pat]=Edge(v,head[u]),head[u]=pat; 21 e[++pat]=Edge(u,head[v]),head[v]=pat; 22 } 23 /************* 模板 ********************/ 24 inline void split(int k){ 25 int len=0,i; 26 while(k) d[++len]=k&1,k>>=1; 27 for(i=len+1;i<=18;++i) d[i]=0; 28 } 29 inline void update(int& root,int las){ 30 int now=root=++cnt; 31 sum[now]=sum[las]+1; 32 for(int i=18;i;--i){ 33 to[now][d[i]^1]=to[las][d[i]^1], 34 to[now][d[i]]=++cnt,las=to[las][d[i]], 35 now=cnt,sum[now]=sum[las]+1; 36 } 37 } 38 #define v e[i].to 39 void dfs1(int u,int fa){ 40 siz[u]=1,son[u]=top[u]=0; 41 split(val[u]),update(rt[u],rt[fa]); 42 for(int i=head[u];i;i=e[i].next) if(v!=fa){ 43 f[v]=u,dep[v]=dep[u]+1,dfs1(v,u),siz[u]+=siz[v]; 44 if(siz[v]>siz[son[u]]) son[u]=v; 45 } 46 } 47 void dfs2(int u){ 48 if(!top[u]) top[u]=u; if(!son[u]) return ; 49 top[son[u]]=top[u],dfs2(son[u]); 50 for(int i=head[u];i;i=e[i].next) 51 if(v!=son[u] && v!=f[u]) dfs2(v); 52 } 53 #undef v 54 inline int LCA(int u,int v){ 55 while(top[u]^top[v]) 56 dep[top[u]]>dep[top[v]]?u=f[top[u]]:v=f[top[v]]; 57 return dep[u]<dep[v]?u:v; 58 } 59 /* 程序 */ 60 inline int query(int u,int v,int lca,int f_lca){ 61 int ans=0; 62 for(int i=18;i;--i){ 63 if(sum[to[u][d[i]^1]]+sum[to[v][d[i]^1]]-sum[to[lca][d[i]^1]]-sum[to[f_lca][d[i]^1]]) 64 ans|=(1<<i-1),u=to[u][d[i]^1],v=to[v][d[i]^1],lca=to[lca][d[i]^1],f_lca=to[f_lca][d[i]^1]; 65 else u=to[u][d[i]],v=to[v][d[i]],lca=to[lca][d[i]],f_lca=to[f_lca][d[i]]; 66 } return ans; 67 } 68 int x,y,z,lca; 69 inline void query(){ 70 x=read(),y=read(),z=read(),lca=LCA(x,y),split(z); 71 printf("%d\n",query(rt[x],rt[y],rt[lca],rt[f[lca]])); 72 } 73 int main(){ 74 while(~scanf("%d%d",&n,&m)){ 75 pat=cnt=0,memset(head,0,sizeof(head)); 76 for(int i=1;i<=n;++i) val[i]=read(); 77 for(int i=1,u,v;i<n;++i) 78 u=read(),v=read(),add(u,v); 79 dfs1(1,0),dfs2(1); while(m--) query(); 80 } return 0; 81 }
而後就是這題(TM作了我一夜就在那裏 TLE、 MLE 、WA 各類掛): L
這道題...夠噁心的,又是區間內詢問區間...
並且更噁心的是,你要用分塊的算法去優化算法...難以想到(其實打起來也還好)
$a[i]$ 表明 1 ~ i 的 前綴異或和
$f[i][j]$ 表明以第 i * block 這個位置開始,到 j-1 結束的區間內的前綴異或和中,與 a[j] 異或的最大值
1 //by Judge 2 #include<cmath> 3 #include<cstdio> 4 #include<iostream> 5 #define ll long long 6 using namespace std; 7 const int M=12111; 8 char buf[1<<20],*p1,*p2; 9 #define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++) 10 inline int read(){ 11 int x=0,f=1; char c=getchar(); 12 for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 13 for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 14 } 15 char sr[1<<21],z[20];int C=-1,Z; 16 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 17 inline void print(ll x){ 18 if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 19 while(z[++Z]=x%10+48,x/=10); 20 while(sr[++C]=z[Z],--Z);sr[++C]='\n'; 21 } 22 int n,m,block,cnt,a[M<<1],f[311][M]; 23 int d[50],rt[M<<1],to[M<<6][2],sum[M<<6]; 24 inline void split(int k){ 25 int len=0; while(k) d[++len]=k&1,k>>=1; 26 for(int i=len+1;i<=32;++i) d[i]=0; 27 } 28 inline void update(int& root,int las){ 29 int now=root=++cnt; sum[now]=sum[las]+1; 30 for(int i=32;i;--i){ 31 to[now][d[i]^1]=to[las][d[i]^1]; 32 to[now][d[i]]=++cnt,las=to[las][d[i]]; 33 sum[now=cnt]=sum[las]+1; 34 } 35 } 36 inline ll query(int u,int v){ 37 ll ans=0; 38 for(int i=32;i;--i){ 39 if(sum[to[v][d[i]^1]]-sum[to[u][d[i]^1]]) 40 ans|=1ll<<i-1,u=to[u][d[i]^1],v=to[v][d[i]^1]; 41 else u=to[u][d[i]],v=to[v][d[i]]; 42 } return ans; 43 } 44 int main(){ 45 n=read(),m=read(),update(rt[0],0); int x,y,l,r,s,i,j; ll ans=0; 46 for(i=1;i<=n;++i) a[i]=read()^a[i-1],split(a[i]),update(rt[i],rt[i-1]); 47 for(block=(int)sqrt(n+1)+1,i=0;i<=n;i+=block) for(j=i+1;j<=n;++j) 48 split(a[j]),f[i/block][j]=max(1ll*f[i/block][j-1],query(i?rt[i-1]:0,rt[j-1])); 49 while(m--){ 50 x=read(),y=read(), 51 r=max((1ll*x+ans)%n+1,(1ll*y+ans)%n+1), 52 s=l=min((1ll*x+ans)%n+1,(1ll*y+ans)%n+1)-1; 53 while(s%block && s<r) ++s; 54 if(s==r){ 55 for(ans=0,j=l+1;j<=r;++j) 56 split(a[j]),ans=max(ans,query(l?rt[l-1]:0,rt[j-1])); 57 } else{ 58 for(ans=f[s/block][r],j=s-1;j>=l;--j) 59 split(a[j]),ans=max(ans,query(rt[j],rt[r])); 60 } print(ans); 61 } Ot(); return 0; 62 }
其實這是一道省選題(已填坑): Alo
題目說的就是要找出一個區間,讓該區間內的次大值異或上區間內的任意一個數,使得異或和最大
坑... set 來維護已出現的下標,可是在使用 set 前竟然要加入 -一、-二、inf、inf+1 四個元素...
以防止訪問越界的狀況(咱們是依次枚舉那個次大值,而後要找到前、後比他大的第二近的元素下標,也就是說容易越界)
而後這裏仍是要用到可(e)愛(xin)的前綴異或和
//by Judge #include<algorithm> #include<iostream> #include<cstdio> #include<set> using namespace std; const int M=1e5+111; const int inf=1e9+7; char buf[1<<20],*p1,*p2; #define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++) inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int n,cnt,ans; set<int> q; int d[40],rt[M],sum[M<<5],son[M<<5][2]; struct Node{ int id,val; }a[M]; inline bool operator <(Node& a,Node& b){ return a.val>b.val; } inline void split(int k){ int len=0; while(k) d[++len]=k&1,k>>=1; for(int i=len+1;i<=30;++i) d[i]=0; } inline void update(int& nw,int las){ int now=nw=++cnt; sum[now]=sum[las]+1; for(int i=30;i;--i){ son[now][d[i]^1]=son[las][d[i]^1]; son[now][d[i]]=++cnt,las=son[las][d[i]]; sum[now=cnt]=sum[las]+1; } } inline int query(int u,int v){ int ans=0; for(int i=30;i;--i){ if(sum[son[v][d[i]^1]]-sum[son[u][d[i]^1]]) ans|=(1<<i-1),u=son[u][d[i]^1],v=son[v][d[i]^1]; else u=son[u][d[i]],v=son[v][d[i]]; } return ans; } int main(){ n=read(); for(int i=1;i<=n;++i) a[i].val=read(),a[i].id=i; for(int i=1;i<=n;++i) split(a[i].val),update(rt[i],rt[i-1]); q.insert(-1),q.insert(inf),q.insert(-2),q.insert(inf+1), sort(a+1,a+1+n),q.insert(a[1].id); for(int i=2;i<=n;++i){ int l=a[i].id,r=a[i].id,x=a[i].id; set<int>::iterator t,p; t=p=q.lower_bound(x); ++t,r=*t-1,--p,--p,l=*p+1,l=max(1,l),r=min(r,n),q.insert(x); if(l^r) split(a[i].val),ans=max(ans,query(rt[l-1],rt[r])); } printf("%d\n",ans); return 0; }
這裏用的就是set ,不過你手打 splay 也是沒問題的
emmmmm...可持久化 trie 的題仍是蠻少的...