真实的树上启发式合并:先dfs一遍,处理出轻儿子和重儿子,然后先处理轻儿子,再处理重儿子,最后把轻儿子合并进重儿子。
虚假的树上启发式合并:大的合进小的.jpg
然而虚假的启发式合并的时间复杂度意外的优秀,很意外的能水(稳?)过很多题。
比起真实的启发式合并少了蛮多代码,何乐而不为呢。
同时空间复杂度居然也优化了不少,swap操作让全局map内元素少了非常非常多。
cf600e:
#include<bits/stdc++.h>
using namespace std;
const long long mod = 1e9 + 7;
typedef long long ll;
const int maxn = 1e5 + 5;
int n, u, v, cor[maxn];
ll ans[maxn];
int id[maxn], tot;
unordered_map<int, int> mp[maxn];
map<int, ll> cnt[maxn];
vector<int> G[maxn];
void merge(int &x, int &y) {
if (mp[y].size() > mp[x].size()) {
swap(x, y);
}
for (auto &e: mp[y]) {
cnt[x][mp[x][e.first]] -= e.first;
mp[x][e.first] += e.second;
cnt[x][mp[x][e.first]] += e.first;
}
}
void dfs(int x, int fa) {
id[x] = ++tot;
mp[id[x]][cor[x]] = 1;
cnt[id[x]][mp[id[x]][cor[x]]] = cor[x];
for (int i = 0; i < G[x].size(); ++i) {
int v = G[x][i];
if (v == fa) {
continue;
}
dfs(v, x);
merge(id[x], id[v]);
}
ans[x] = cnt[id[x]].rbegin()->second;
}
int main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> cor[i];
}
for (int i = 1; i < n; ++i) {
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 1);
for (int i = 1; i <= n; ++i) {
cout << ans[i] << ' ';
}
return 0;
}