樹的啓發式合併能夠解決不少不涉及修改的子樹查詢問題。c++
每次向上合併時,輕鏈併入重鏈,可使得總複雜度由\(O(n^2)\)變成\(O(n\log(n))\)。由於每次加入重鏈,子樹大小都會翻倍。spa
給定一棵樹,每一個節點都有一個顏色值。
定義一種顏色值佔領一棵子樹,當且僅當這種顏色是這棵子樹中出現最多的顏色。code
問每一個節點爲根的子樹中,佔領這棵子樹的顏色值之和。it
#include <bits/stdc++.h> #define FOPI freopen("in.txt", "r", stdin) #define FOPO freopen("out.txt", "w", stdout) using namespace std; typedef long long LL; typedef pair<int, int> pr; const int maxn = 1e5 + 100; int n; vector<int> v[maxn]; map<int, int> col[maxn]; LL ans[maxn]; int maxt[maxn]; void merge(int x, int y) { if (col[x].size() < col[y].size()) { swap(col[x], col[y]); ans[x] = ans[y]; maxt[x] = maxt[y]; } for (auto val : col[y]) { int a = val.first, b = val.second; col[x][a] += b; if (col[x][a] > maxt[x]) { maxt[x] = col[x][a]; ans[x] = a; } else if (col[x][a] == maxt[x]) { ans[x] += a; } } col[y].clear(); } void dfs(int x, int from) { for (int y : v[x]) { if (y == from) continue; dfs(y, x); merge(x, y); } } int main() { //FOPI; scanf("%d", &n); int x, y; for (int i = 1; i <= n; i++) { scanf("%d", &x); col[i][x] = 1; ans[i] = x; maxt[i] = 1; } for (int i = 1; i <= n-1; i++) { scanf("%d%d", &x, &y); v[x].push_back(y), v[y].push_back(x); } dfs(1, 0); printf("%lld", ans[1]); for (int i = 2; i <= n; i++) printf(" %lld", ans[i]); puts(""); }