树的启发式合并能够解决不少不涉及修改的子树查询问题。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(""); }