/*
樹上啟發式合併
用於處理離線查詢的子樹問題
第一次先求出重兒子
第二次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的迭代器,所以這種寫...