樹鏈剖分換根

2021-09-27 03:32:39 字數 3994 閱讀 8135

描述

給定一棵 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個城市,這些城市之間由一些路連線且這些城市構成了...