Codeforces 600E dsu on tree 啟發式合併
阿新 • • 發佈:2018-12-14
題目:傳送門
題意:
給一顆樹,計算所有結點的子樹,顏色最多的結點的 顏色種類的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;
}