描述
給定一棵 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 的操作,輸出一行乙個整數表示答案。
這道題總體很板,不過換根的操作很清奇,我們來詳細討論一下。
可以發現換根對路徑查詢&修改沒有影響。
我們考慮不改變樹的形狀,分類討論換根對子樹查詢&修改的影響:
1、如果u=r
oo
tu=root
u=root
,則直接操作整棵樹。
2、如果lca
(u,r
oot)
≠u
lca(u,root)\not=u
lca(u,
root
)=
u,即根不在u
uu的子樹內,則根對子樹的操作無影響。
3、如果lca
(u,r
oot)
=u
lca(u,root)=u
lca(u,
root
)=u,即根在u
uu的子樹內,我們定義v
vv為roo
troot
root
到u
uu的樹上路徑上的最後乙個點,則v
vv的子樹是對於全集的操作集的補集。
分上面三種情況討論,這道題就可以當板子來做了。
#include
#include
#define int long long
using
namespace std;
const
int maxn =
100005
;int
read()
int n,m,tot,root=
1,a[maxn]
,f[maxn]
,fa[maxn]
,siz[maxn]
,num[maxn]
;int index,top[maxn]
,c[maxn]
,son[maxn]
,dep[maxn]
,tr[maxn*4]
,lazy[maxn*4]
;struct edge
e[2*maxn]
;void
dfs1
(int u,
int par)
}void
dfs2
(int u,
int tp)
intlca
(int x,
int y)
return dep[x]
>dep[y]
?y:x;
}int
find
(int x,
int y)
if(dep[x]
)swap
(x,y)
;return son[y];}
void
build
(int i,
int l,
int r)
int mid=
(l+r)
>>1;
build
(i<<
1,l,mid)
;build
(i<<1|
1,mid+
1,r)
; tr[i]
=tr[i<<1]
+tr[i<<1|
1];}
void
down
(int i,
int l,
int mid,
int r)
void
updata
(int i,
int l,
int r,
int l,
int r,
int x)
if(rreturn
;int mid=
(l+r)
>>1;
down
(i,l,mid,r)
;updata
(i<<
1,l,mid,l,r,x)
;updata
(i<<1|
1,mid+
1,r,l,r,x)
; tr[i]
=tr[i<<1]
+tr[i<<1|
1];}
intquery
(int i,
int l,
int r,
int l,
int r)
void
modify
(int u,
int v,
int x)
if(dep[u]
<=dep[v]
)swap
(u,v)
;updata(1
,1,n,num[v]
,num[u]
,x);
}int
ask(
int u,
int v)
if(dep[u]
<=dep[v]
)swap
(u,v)
; ans+
=query(1
,1,n,num[v]
,num[u]);
return ans;
}signed
main()
,f[j]
=tot;
e[++tot]
=edge
,f[i]
=tot;
}dfs1(1
,0);
dfs2(1
,1);
build(1
,1,n);
m=read()
;while
(m--)if
(op==3)
if(lca==u)
else
updata(1
,1,n,num[u]
,num[u]
+siz[u]-1
,c);}if
(op==4)
printf
("%lld\n"
,ask
(u,read()
));if
(op==5)
int lca=
lca(root,u)
,ans;
if(lca==u)
else
ans=
query(1
,1,n,num[u]
,num[u]
+siz[u]-1
);printf
("%lld\n"
,ans);}
}}
樹鏈剖分 樹剖換根
這是一道模板題。給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有乙個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別 換根 將乙個指定的節點設定為樹的新根。修改路徑權值 給定兩個節點,將這兩個節點間路徑上的所有節點權值 含這兩個節點 增加乙個給定的值。修改子樹權值 給定乙...
2018 08 27 模板 樹鏈剖分換根(模板)
描述 給定一棵大小為 n 的有根點權樹,支援以下操作 換根 修改點權 查詢子樹最小值 輸入第一行兩個整數 n,q 分別表示樹的大小和運算元。接下來n行,每行兩個整數f,v,第i 1行的兩個數表示點i的父親和點i的權。保證f i。如 果f 0,那麼i為根。輸入資料保證只有i 1時,f 0。接下來 m ...
遙遠的國度(樹鏈剖分,換根)
zcwwzdjn在追殺十分sb的zhx,而zhx逃入了乙個遙遠的國度。當zcwwzdjn準備進入遙遠的國度繼續追殺時,守護神rapid阻攔了zcwwzdjn的去路,他需要zcwwzdjn完成任務後才能進入遙遠的國度繼續追殺。問題是這樣的 遙遠的國度有n個城市,這些城市之間由一些路連線且這些城市構成了...