# 树上启发式合并 dsu on tree

22 篇文章 1 订阅

dsu on tree 用来解决树上问题。可以在 O ( n log ⁡ n ) O(n \log n) 中完成对静态的子树统计。但是，不支持修改，只能对子树统计，不能链上统计。

void add(int x, int fa, int val) {
cnt[col[x]] += val;
if(cnt[col[x]] > mx) mx = cnt[col[x]], sum = col[x];
else if(cnt[col[x]] == mx) sum += col[x];
for(int i = 0; i < G[x].size(); i++) {
int y = G[x][i];
if (y != fa) add(y, x, val);
}
}
void dfs(int x, int fa) {
for(int i = 0; i < G[x].size(); i++) {
int y = G[x][i];
if(y != fa) dfs(y, x);
}
add(x, fa, 1); ans[x] = sum;
add(x, fa, -1), sum = 0, mx = 0;
}


1. 遍历每一个节点
2. 递归解决所有的轻儿子，同时消除递归产生的影响
3. 递归重儿子，不消除递归的影响
4. 暴力统计所有轻儿子对答案的影响
5. 更新该节点的答案
6. 暴力删除所有轻儿子对答案的影响

#include <bits/stdc++.h>
using namespace std;
#define re register
#define F first
#define S second
#define mp make_pair
#define lson (p << 1)
#define rson (p << 1 | 1)
typedef long long ll;
typedef pair<int, int> P;
const int N = 5e5 + 5, M = 5e5 + 5;
const int INF = 0x3f3f3f3f;
int X = 0,w = 0; char ch = 0;
while(!isdigit(ch)) {w |= ch == '-';ch = getchar();}
while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48),ch = getchar();
return w ? -X : X;
}
inline void write(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
int n, val[N], cnt[N], mx;
ll sum, ans[N];
struct edge{
int to, nxt;
}e[M];
}
int sz[N], son[N];
void dfs1(int x, int fa){
sz[x] = 1;
for (int i = head[x]; i; i = e[i].nxt){
int y = e[i].to;
if (y != fa){
dfs1(y, x); sz[x] += sz[y];
if (sz[y] > sz[son[x]]) son[x] = y;
}
}
}
bool vis[N];
void add(int x, int fa, int k){
cnt[val[x]] += k;
if (k > 0 && cnt[val[x]] > mx) sum = val[x], mx = cnt[val[x]];
else if (k > 0 && cnt[val[x]] == mx) sum += val[x];
for (int i = head[x]; i; i = e[i].nxt){
int y = e[i].to;
if (y != fa && !vis[y]) add(y, x, k);
}
}
void dfs2(int x, int fa, int flg){
for (int i = head[x]; i; i = e[i].nxt){
int y = e[i].to;
if (y != fa && y != son[x]) dfs2(y, x, 0);
}
if (son[x]) dfs2(son[x], x, 1), vis[son[x]] = 1;
add(x, fa, 1); ans[x] = sum;
if (son[x]) vis[son[x]] = 0;
if (!flg) add(x, fa, -1), sum = mx = 0;
}
int main() {
for (int i = 1; i <= n; i++) val[i] = read();
for (int i = 1; i < n; i++){
}
dfs1(1, 0); dfs2(1, 0, 0);
for (int i = 1; i <= n; i++) printf("%lld ", ans[i]);
return 0;
}

• 1
点赞
• 0
收藏
觉得还不错? 一键收藏
• 0
评论
01-01 410
09-29 2016
10-27 436
02-12 369
11-08 8895
03-24
11-07
12-17 899
09-13 371

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。