遙遠的國度 (樹鏈剖分換根),洛谷P3979
阿新 • • 發佈:2021-07-10
析:顯然,若沒有換根操作,則為樹鏈剖分板子題,但是這道題我們考慮換根操作
考慮這樣一個性質:在一棵樹上,兩點的距離路徑是唯一的!!
也就是說,我們在修改路徑上的點權時,不必考慮根在哪裡,直接利用模板修改即可
麻煩的地方在於查詢操作,我分了三種情況來討論:(設 root 為當前的根,id 為當前要查詢的編號)
1.若 deep[id]>=deep[root] ,則跟對他沒有影響,直接查詢即可
2.若 deep[id]<deep[root] ,剛開始我是求出兩點的 LCA ,然後查詢 (1,num[lca]) 和 (num[root]+size[root],n)
但是,這樣會出現一些問題,比如這張圖:
當 root=7,id==5 時,我們這樣查詢就會漏掉 6
那麼我們能不能這樣寫呢 ? 查詢(1,num[root]-1) 和(num[root]+size[root],n);
顯然也是不能的(雖然這資料比較水,這樣寫會多得幾分),如下圖:
當 root==4 ,id==2 時,這樣查詢就會多出來一個點 3
這...........可如何是好?
其實,應該有大佬已經發現了,無論是什麼情況,我們查詢的邊界始終是 (1,num[son[lca]]-1)和 (num[son[lca]+size[son[lca]]] , n)
所以,我們只需查詢 root 和 id 的 lca 的重兒子即可(保證在同一條鏈上)
程式碼如下:
#include<bits/stdc++.h> #define re register int #define next nett #define lc rt<<1 #define rc rt<<1|1 #define mid ((l+r)>>1) #define ii inline int #define iv inline void using namespace std; const int N=1e6+10; struct CUN { int minn,lazy; }use[N<<4]; int n,m,root,timi,tot,lcas; int to[N<<1],next[N<<1],head[N],num[N],top[N],son[N],deep[N],fa[N],size[N],chu[N],zh[N]; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=0; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return (f)?x:(-x); } ii add(int x,int y) { to[++tot]=y; next[tot]=head[x]; head[x]=tot; } iv dfs1(int st,int f) { size[st]=1; fa[st]=f; deep[st]=deep[f]+1; for(re i=head[st];i;i=next[i]) { int p=to[i]; if(p==f) continue; dfs1(p,st); size[st]+=size[p]; son[st]=(size[son[st]]>size[p])?son[st]:p; } } iv dfs2(int st,int t) { top[st]=t; num[st]=++timi; zh[timi]=chu[st]; if(!son[st]) return; dfs2(son[st],t); for(re i=head[st];i;i=next[i]) { int p=to[i]; if(p==fa[st]||p==son[st]) continue; dfs2(p,p); } } iv pp(int rt) { use[rt].minn=min(use[lc].minn,use[rc].minn); } iv build(int rt,int l,int r) { if(l==r) { use[rt].minn=zh[l]; return; } build(lc,l,mid); build(rc,mid+1,r); pp(rt); } ii gett(int x,int y) { int fx=top[x],fy=top[y]; while(fx!=fy) { if(deep[fx]<deep[y]) swap(x,y),swap(fx,fy); if(fa[fx]==y) return fx; x=fa[fx]; fx=top[x]; } if(deep[x]>deep[y]) swap(x,y); return son[x]; } iv pd(int rt,int l,int r) { if(use[rt].lazy) { use[lc].minn=use[rt].lazy; use[rc].minn=use[rt].lazy; use[lc].lazy=use[rc].lazy=use[rt].lazy; use[rt].lazy=0; } } iv change(int rt,int l,int r,int L,int R,int z) { if(R<l||r<L||L>R) return ; if(L<=l&&r<=R) { use[rt].minn=z; use[rt].lazy=z; return; } pd(rt,l,r); if(mid>=L) change(lc,l,mid,L,R,z); if(mid<R) change(rc,mid+1,r,L,R,z); pp(rt); } ii query(int rt,int l,int r,int L,int R) { if(R<l||r<L||L>R) return 999999999; if(L<=l&&r<=R) return use[rt].minn; pd(rt,l,r); if(mid>=R) return query(lc,l,mid,L,R); if(mid<L) return query(rc,mid+1,r,L,R); return min(query(lc,l,mid,L,R),query(rc,mid+1,r,L,R)); } iv upd(int x,int y,int z) { int fx=top[x],fy=top[y]; while(fx!=fy) { if(deep[fx]<deep[fy]) swap(x,y),swap(fx,fy); change(1,1,n,num[fx],num[x],z); x=fa[fx]; fx=top[x]; } if(deep[x]>deep[y]) swap(x,y); change(1,1,n,num[x],num[y],z); } int main() { n=read(); m=read(); int a,b,c,d,lca; for(re i=1;i<n;i++) { a=read(); b=read(); add(a,b); add(b,a); } for(re i=1;i<=n;i++) chu[i]=read(); root=1; dfs1(root,root); dfs2(root,root); build(1,1,n); root=read(); while(m--) { a=read(); if(a==1) { root=read(); continue; } if(a==2) { b=read(); c=read(); d=read(); upd(b,c,d); } if(a==3) { b=read(); if(b==root) { printf("%d\n",query(1,1,n,1,n)); continue; } lcas=gett(b,root); if(deep[b]>=deep[root]) printf("%d\n",query(1,1,n,num[b],num[b]+size[b]-1)); else if(fa[lcas]==b) printf("%d\n",min(query(1,1,n,1,num[lcas]-1),query(1,1,n,num[lcas]+size[lcas],n))); else printf("%d\n",query(1,1,n,num[b],num[b]+size[b]-1)); continue; } } return 0; }