树上启发式合并
树上启发式合并,是在统计信息的复杂度较高,没有办法每个点开一个信息空间的时候,考虑将信息空间公用,同时尽量保持时空复杂度平衡的方法。
例题:
CF600E
给定一棵有根树,每个点有一个颜色。对于每一个点为根的子树,求子树内出现次数最多的颜色的和(可并列)。
\(n \le 10^5, c_i \le n\)
思路分析:
https://zhuanlan.zhihu.com/p/565967113
时间复杂度 \(O(n \log n)\)。
代码实现:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
int n;
int hson[100010];
int c[100010];
vector<int> g[100010];
int sz[100010];
int ans[100010], maxt, cnt[100010];
void fhson(int now, int fa) {
sz[now] = 1;
int mx = 0;
f(i, 0, (int)g[now].size() - 1) {
if(g[now][i] != fa) {
fhson(g[now][i], now);
sz[now] += sz[g[now][i]];
if(sz[g[now][i]] > mx) {
mx = sz[g[now][i]];
hson[now] = g[now][i];
}
}
}
}
int sum = 0;
void add(int now) {
cnt[c[now]]++;
if(cnt[c[now]] > maxt) {
maxt = cnt[c[now]];
sum = c[now];
}
else if(cnt[c[now]] == maxt){
sum += c[now];
}
}
void del(int now) {
--cnt[c[now]];
sum = 0; maxt = 0;
}
void asubtree(int now, int fa) {
add(now);
f(i, 0, (int)g[now].size()- 1) {
if(g[now][i] != fa) asubtree(g[now][i], now);
}
return;
}
void dsubtree(int now, int fa) {
del(now);
f(i, 0, (int)g[now].size()- 1) {
if(g[now][i] != fa) dsubtree(g[now][i], now);
}
return;
}
void dfs(int now, int fa, bool keep) {
f(i, 0, (int)g[now].size()- 1) {
if(g[now][i] == fa ) continue;
if(g[now][i] == hson[now]) continue;
dfs(g[now][i], now, 0);
}
if(hson[now])dfs(hson[now], now, 1);
add(now);
f(i, 0, (int) g[now].size() - 1) {
if(g[now][i] == fa ) continue;
if(g[now][i] == hson[now]) continue;
asubtree(g[now][i], now);
}
ans[now] = sum;
if(!keep) {
dsubtree(now, fa);
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
time_t start = clock();
//think twice,code once.
//think once,debug forever.
cin >> n;
f(i, 1, n) {
cin >> c[i];
}
f(i, 1, n - 1) {
int u, v; cin >> u >> v;
g[u].push_back(v);g[v].push_back(u);
}
fhson(1, 0);
dfs(1, 0, 0);
f(i,1 ,n) cout << ans[i] << " ";
time_t finish = clock();
//cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
return 0;
}
细节:
- 分析一下发现
del
函数一旦运行,一定是把整个信息删空了。这时只需要直接把 \(sum\) 和 \(maxt\) 直接初始化即可。