總結 樹上啟發式合併

2022-05-02 12:45:10 字數 3331 閱讀 6558

這種演算法能夠解決關於詢問一棵樹的子樹的相關資訊的問題。

演算法的流程大概是這樣: 

1、dfs將一棵樹建好,將節點的size、dfs序、重兒子、該dfs序對應的節點這些資訊處理好(其他的資訊具體問題具體分析)。 

2、進入solve函式,先去解決非重兒子,然後將這些非重兒子的資訊暴力清空。 

3、接下來解決重兒子,這次不清空。 

4、然後再將非重兒子的資訊再暴力新增。 

5、將該節點的資訊新增進容器。 

6、回答關於這個節點的問題。 

7、返回。 

當然對於存資訊的sum不一定是普通的陣列,可能是map,可能是樹狀陣列這類既支援插入又支援刪除的容器,有適合的資料結構,而不是用資料結構去套題。

複雜度:

對於某個葉子節點來說,我們最終是要把它對答案的貢獻加到根節點中,從而回答根節點的詢問。可以知道,所有的節點其實都在重鏈上,

葉子節點到根節點的最多經過logn條輕邊,我在將這個節點合併到根的時候,也順便將別的節點合併到根上了,每合併一次,複雜度最多o(n)。

總的複雜度應該是o(nlogn)。

例題:codeforces 1009f

#includeusing

namespace

std;

const

int maxn = 1e6 + 5

;int

deep[maxn],num[maxn],son[maxn],ans[maxn],n,mx;

mapmp;

vector

graph[maxn];

void

init()

void add(int cur,int

fa)}

void dfs(int cur,int

fa)

if(son[cur] != -1

) dfs(son[cur],cur);

for(int i = 0;i < graph[cur].size();++i)

mp[deep[cur]] += 1

;

if(mp[deep[cur]] > mp[mx] || (mp[deep[cur]] == mp[mx] && deep[cur] < mx)) mx =deep[cur];

ans[cur] = mx -deep[cur];

}void dfs1(int cur,int fa,intd)}

intmain()

init();

dfs1(

1,0,1

); dfs(

1,0);

for(int i = 1;i <= n;++i) printf("

%d\n

",ans[i]);

return0;

}

codeforces 600e

#includeusing

namespace

std;

typedef

long

long

ll;const

int maxn = 1e5 + 5

;ll color[maxn],ans[maxn];

mapint>mp;

mapmp2;

vector

graph[maxn];

intdeep[maxn],num[maxn],son[maxn],mx;

//map::iterator iter;

void add(int cur,int

fa)}

void dfs(int cur,int

fa)

if(son[cur] != -1

) dfs(son[cur],cur);

for(int i = 0;i < graph[cur].size();++i)

int keep = ++mp[color[cur]];

if(keep > mx) mx =keep;

mp2[keep] +=color[cur];

ans[cur] =mp2[mx];

}void dfs1(int cur,int fa,intd)}

intmain()

dfs1(

1,0,1

); dfs(

1,0);

for(int i = 1;i <= n;++i)

printf(

"%lld%c

",ans[i],i == n ? '

\n' : '');

return0;

}

codeforces 570d

#includeusing

namespace

std;

const

int maxn = 5e5 + 5

;struct

query

};int

deep[maxn],son[maxn],num[maxn],color[maxn],n,m;

char

str[maxn];

bool

ans[maxn];

mapmp;

vector

graph[maxn];

vector

ask[maxn];

inline

int cnt1(int

x)

return

ret;

}void add(int cur,int

fa)}

void dfs(int cur,int

fa)

if(son[cur] != -1

) dfs(son[cur],cur);

for(int i = 0;i < graph[cur].size();++i)

mp[deep[cur]] ^= (1

}void dfs1(int cur,int fa,intd)}

intmain()

scanf("%s

",str + 1

);

for(int i = 1;i <= n;++i) color[i] = str[i] - 'a'

;

for(int i = 0;i < m;++i)

memset(son,-1,sizeof

(son));

dfs1(

1,0,1

); dfs(

1,0);

for(int i = 0;i < m;++i)

}

樹上啟發式合併總結

某一天發現一道樹上啟發式合併裸題,但我不會寫 學習並刷了兩天的題,是時候來寫個總結了 樹上啟發式合併 dsu on tree 是乙個在o n logn o nlogn o nlog n 時間內解決許多樹上問題的有力演算法。但它的中心其實是 暴力!沒錯,它正是由暴力優化而來。我們先看一道例題 cf60...

樹上啟發式合併

解決樹上統計問題,o n log n o n log n o n lo g n 可以結合線段樹等資料結構維護深度上的資訊 部落格 入門題 const int maxn 1e5 7 const int mod 1e9 7 ll n,m,u,v,mx,sum vector int mp maxn int...

樹上啟發式合併

樹上啟發式合併,一種美妙的黑科技,可以用普通的優化讓你 n 2 變成嚴格 n log 解決一些類似 樹上數顏色,樹上查眾數 這樣的問題 首先你要知道暴力為什麼是 n 2 的 以這個圖為例 每次你從乙個節點開始向下搜,你從1節點搜到3,搜完這個子樹然後你需要把3存的col等資訊刪去再遍歷另乙個子樹才是...