dsu on tree 樹上啟發式合併

2021-10-24 04:00:24 字數 1595 閱讀 3551

/*

樹上啟發式合併

用於處理離線查詢的子樹問題

第一次先求出重兒子

第二次dfs的過程中,先計算輕兒子,貢獻不保留,再計算重兒子,貢獻保留

最後再加上輕兒子的貢獻,計算出當前節點的值

複雜度為o(nlogn)

*//*

每個節點都有乙個顏色編號

計算一棵以1為根的樹的以每個節點為根的子樹中顏色最多的顏色編號和

這題中子樹的貢獻就在於cnt陣列,用來計數顏色出現了多少次

*/#include

#include

using

namespace std;

const

int maxn =

1e5+5;

typedef

long

long ll;

vector<

int> g[maxn]

;int c[maxn]

,cnt[maxn]

,son[maxn]

,siz[maxn]

;ll ans[maxn]

;void

dfs1

(int x,

int f)

//找重兒子 }}

int flag =0;

ll sum =

0,maxc =0;

//當前的答案和最大值

void

count

(int x,

int f,

int val)

//將以x為根的子樹的貢獻加上val但不包括flag的貢獻

else

if( cnt[c[x]

]== maxc )

//兩個顏色都為最大值都得算

for(

int i =

0;i < g[x]

.size()

; i++)}

void

dfs2

(int x,

int f,

int keep)

//當前節點為x,父親節點為f,keep為false表示不保留貢獻

if( son[x]

)count

(x,f,1)

;//將輕兒子和自身的貢獻加上

ans[x]

= sum;

flag =0;

if(!keep )

//沒消去貢獻時,maxc與sum都是有用的

}int

main()

for(

int i =

1; i < n; i++

)dfs1(1

,0);

dfs2(1

,0,false);

for(

int i =

1; i <= n; i++

)return0;

}

證明複雜度:

對於乙個節點需要再次被計算,當且僅當它的某個祖先節點不為其父親節點的重兒子,假設這個節點被計算了logn次,由於有重兒子的存在,每次計算都會附帶乙個重兒子,節點數每次至少乘2,logn次後就達到了n,所以乙個節點被計算次數不超過logn。

dsu on tree 樹上啟發式合併

詳解 dsu on tree 樹上啟發式合併 演算法總結 習題 經典例題 題意 一棵樹有n個結點,每個結點都是一種顏色,每個顏色有乙個編號,求樹中每個子樹的最多的顏色編號的和。dsu on tree簡介 在o n 2 的暴力做法中,我們用cnt記錄每種顏色出現的次數,對於每個結點,遍歷這棵子樹上的所...

dsu on tree 樹上啟發式合併

對於一顆靜態樹,o nlogn 時間內處理子樹的統計問題。是一種優雅的暴力。很顯然,樸素做法下,對於每顆子樹對其進行統計的時間複雜度是平方級別的。考慮對樹進行乙個重鏈剖分。雖然都基於重鏈剖分,但不同於樹剖,我們維護的不是樹鏈。對於每個節點,我們先處理其輕兒子所在子樹,輕子樹在處理完後消除其影響。然後...

學習總結 Dsu on tree 樹上啟發式合併

rt,這只是一篇小小的總結,以便將來的回顧,並不詳細講 以前也學習過啟發式合併,大概就是像樹形dp一樣在dfs上,將兒子的資訊向父親轉移,容器是map,將兒子的資訊邊轉移邊更新答案,轉移之後便將兒子的容器清空,防止空間超限。不過對於本人而言,雖然思路較為簡便,但是因為有用到map的迭代器,所以這種寫...