1. 程式人生 > >【模板】樹鏈剖分+換根

【模板】樹鏈剖分+換根

樹鏈剖分

描述

給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有一個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別:

換根:將一個指定的節點設定為樹的新根。

修改路徑權值:給定兩個節點,將這兩個節點間路徑上的所有節點權值(含這兩個節點)增加一個給定的值。

修改子樹權值:給定一個節點,將以該節點為根的子樹內的所有節點權值增加一個給定的值。

詢問路徑:詢問某條路徑上節點的權值和。

詢問子樹:詢問某個子樹內節點的權值和。

輸入

第一行為一個整數 n,表示節點的個數。 第二行 n 個整數表示第i個節點的初始權值 ai 第三行 n−1 個整數,表示i+1 號節點的父節點編號 fi+1 (1⩽fi+1⩽n) 第四行一個整數 m,表示操作個數。 接下來 m 行,每行第一個整數表示操作型別編號:(1⩽u,v⩽n) 若型別為 1,則接下來一個整數 u,表示新根的編號。 若型別為 2,則接下來三個整數 u,v,k,分別表示路徑兩端的節點編號以及增加的權值。 若型別為3,則接下來兩個整數 u,k,分別表示子樹根節點編號以及增加的權值。 若型別為 4,則接下來兩個整數u,v,表示路徑兩端的節點編號。 若型別為 5,則接下來一個整數 u,表示子樹根節點編號。

輸出

對於每一個型別為 4 或 5 的操作,輸出一行一個整數表示答案。

樣例輸入

6 1 2 3 4 5 6 1 2 1 4 4 6 4 5 6 2 2 4 1 5 1 1 4 3 1 2 4 2 5

樣例輸出

15 24 19

提示

對於 100% 的資料,1⩽n,m,k,ai⩽105。資料有一定梯度。

分析

是個很好的板子題,集結了樹鏈剖分的大部分操作,而且還有個新操作換根,妙啊

重點講一下如何換根。 我們很容易發現換根的操作對路徑修改&查詢是沒有影響的 只對子樹的操作有影響 考慮root為當前的根,u為所求子樹的根,lca為原圖中的lca 1.u==root,那麼u 的子樹就是整棵樹。 2.LCA(root,u)≠u,即root不在u的子樹中。那麼u現在的子樹就是原來的子樹 3.LCA(root,u)=u,即u 在原來的樹中是root 的祖先。那麼我們找到u 到root 路徑上的第一個兒子。這個兒子對應的原樹中的子樹,就是現在u 的子樹的補集。 我們在程式碼中再解釋一番

程式碼

#include<bits/stdc++.h>
#define N 100005
#define lc (k<<1)
#define rc (k<<1)|1
#define in read()
#define int long long
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=
(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return f==1?res:-res; } int n,m,a[N],root; int top[N],idx[N<<2],pos[N<<2],tot=0; int fa[N],son[N],sze[N],dep[N]; int lzy[N<<2],sum[N<<2]; int nxt[N<<1],to[N<<1],head[N],ecnt=0; inline void add(int x,int y){ nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y; nxt[++ecnt]=head[y];head[y]=ecnt;to[ecnt]=x; } void dfs1(int u,int fu){ sze[u]=1; for(int e=head[u];e;e=nxt[e]){ int v=to[e]; if(v==fu) continue; dep[v]=dep[u]+1;fa[v]=u; dfs1(v,u); sze[u]+=sze[v]; if(sze[v]>sze[son[u]]) son[u]=v; } } void dfs2(int u){ if(son[u]){ int v=son[u]; idx[pos[v]=++tot]=v; top[v]=top[u]; dfs2(v); } for(int e=head[u];e;e=nxt[e]){ int v=to[e]; if(v==fa[u]||v==son[u]) continue;// idx[pos[v]=++tot]=v; top[v]=v; dfs2(v); } } inline void build(int k,int l,int r){ if(l==r){ sum[k]=a[idx[l]]; return ; } int mid=l+r>>1; build(lc,l,mid);build(rc,mid+1,r); sum[k]=sum[lc]+sum[rc]; } inline void pushdown(int k,int l,int r){ int mid=l+r>>1; lzy[lc]+=lzy[k];sum[lc]+=(mid-l+1)*lzy[k]; lzy[rc]+=lzy[k];sum[rc]+=(r-mid)*lzy[k]; lzy[k]=0; } inline void modify(int k,int l,int r,int x,int y,int v){ if(x<=l&&r<=y){ lzy[k]+=v;sum[k]+=(r-l+1)*v; return; } if(lzy[k]) pushdown(k,l,r); int mid=l+r>>1; if(x<=mid) modify(lc,l,mid,x,y,v); if(y>mid) modify(rc,mid+1,r,x,y,v); sum[k]=sum[lc]+sum[rc]; } inline void modifypath(int x,int y,int v){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); modify(1,1,n,pos[top[x]],pos[x],v); x=fa[top[x]]; } if(dep[x]<dep[y]) swap(x,y); modify(1,1,n,pos[y],pos[x],v); } inline int getlca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } return dep[x]>dep[y]?y:x; } inline int find(int x,int y){//找 x 到 y 路徑上第一個兒子 while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); if(fa[top[x]]==y) return top[x]; x=fa[top[x]]; } if(dep[x]<dep[y]) swap(x,y); return son[y]; } inline void modifysub(int u,int k){ if(u==root) return modify(1,1,n,1,n,k); int lca=getlca(u,root); if(lca!=u) return modify(1,1,n,pos[u],pos[u]+sze[u]-1,k); else { int child=find(u,root); modify(1,1,n,1,n,k); modify(1,1,n,pos[child],pos[child]+sze[child]-1,-k); return; } } inline int query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y){return sum[k];} int res=0; if(lzy[k]) pushdown(k,l,r); int mid=l+r>>1; if(x<=mid) res+=query(lc,l,mid,x,y); if(y>mid) res+=query(rc,mid+1,r,x,y); return res; } inline int querypath(int x,int y){ int res=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); res+=query(1,1,n,pos[top[x]],pos[x]); x=fa[top[x]]; } if(dep[x]<dep[y]) swap(x,y); res+=query(1,1,n,pos[y],pos[x]); return res; } inline int querysub(int x){ if(x==root) return query(1,1,n,1,n); int lca=getlca(x,root); if(lca!=x) return query(1,1,n,pos[x],pos[x]+sze[x]-1); int child=find(x,root); int res=query(1,1,n,1,n); res-=query(1,1,n,pos[child],pos[child]+sze[child]-1); return res; } signed main(){ n=in;root=1; int i,j,k; for(i=1;i<=n;++i) a[i]=in; for(i=2;i<=n;++i){ j=in;add(i,j); } dfs1(1,0); idx[1]=1;pos[1]=++tot;top[1]=1; dfs2(1); build(1,1,n); m=in;int u,v; while(m--){ int op=in; if(op==1){ u=in;root=u; } else if(op==2){ u=in;v=in;k=in; modifypath(u,v,k); } else if(op==3){ u=in;k=in; modifysub(u,k); } else if(op==4){ u=in;v=in; cout<<querypath(u,v)<<'\n'; } else { u=in; cout<<querysub(u)<<'\n'; } } return 0; }