【模板】樹鏈剖分+換根
樹鏈剖分
描述
給定一棵 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;
}