1. 程式人生 > >Codeforces 600E dsu on tree 啟發式合併

Codeforces 600E dsu on tree 啟發式合併

題目:傳送門

題意:

給一顆樹,計算所有結點的子樹,顏色最多的結點的 顏色種類的sum和,子樹的定義是當前結點和其所有的子節點。

題解:

我們可以想到一種暴力的解法,就是O(n)遍歷所有的結點,然後dfs搜尋它的所有子節點,檢視顏色出現最多的結點,用一個ans陣列維護答案,最後輸出ans。顯然,這樣做的複雜度為O(n^2^) 我們這裡用到dsu on tree演算法,關於這個演算法的詳解,可以參考:http://codeforces.com/blog/entry/44351

AC 程式碼:

#include <bits/stdc++.h>
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f using namespace std; const int maxn = 100010; vector<int> g[maxn]; int Max,col[maxn]; long long sum,ans[maxn]; /****************dsu on tree 演算法************************/ int sz[maxn]; void getsz(int v, int p) {//計算每個結點有多少個子節點 sz[v] = 1; for (auto u : g[v]) if (
u != p) { getsz(u, v); sz[v] += sz[u]; } } int cnt[maxn]; bool big[maxn]; void add(int v, int p, int x) { cnt[ col[v] ] += x; if (cnt[col[v]] > Max) sum = col[v], Max = cnt[col[v]]; else if (cnt[col[v]] == Max) sum += col[v]; for (auto
u : g[v]) if (u != p && !big[u]) add(u, v, x); } void dfs(int v, int p, int keep) {//啟發式搜尋 int mx = -1, bigChild = -1; for (auto u : g[v]) if (u != p && sz[u] > mx) mx = sz[u], bigChild = u; for (auto u : g[v]) if (u != p && u != bigChild) dfs(u, v, 0); if (bigChild != -1) dfs(bigChild, v, 1), big[bigChild] = 1; add(v, p, 1); if (bigChild != -1) big[bigChild] = 0; ans[v] = sum; if (keep == 0) add(v, p, -1), Max = sum = 0; } /****************dsu on tree 演算法************************/ int main(void) { int n, t1, t2; cin >> n; for (int i = 1; i <=n; i++) cin >> col[i]; for (int i = 1; i < n; i++) cin >> t1 >> t2, g[t1].push_back(t2), g[t2].push_back(t1); getsz(1, 0); dfs(1, 0, 1); for (int i = 1; i <= n; i++) cout << ans[i] << " "; return 0; }