[dsu on tree]【学习笔记】

十几天前看到zyf2000发过关于这个的题目的Blog, 今天终于去学习了一下 [Codeforces原文链接](http://codeforces.com/blog/entry/44351#comment-332425)

dsu on tree

简介

我也不清楚dsu是什么的英文缩写...
好吧是Disjoint Set Union 并查集2333
就像是树上的启发式合并
用到了heavylight decomposition树链剖分
把轻边子树的信息合并到重链上的点里
因为每次都是先dfs轻儿子再dfs重儿子,只有重儿子子树的贡献保留,所以可以保证dfs到每颗子树时当前全局维护的信息不会有别的子树里的,和莫队很像


算法过程

find the BigChild of each vertex
dfs(u, fa, keep)
    dfs(LightChild, u, 0)
    dfs(BigChild, u, 1), big[BigChild] = 1
    update(u, fa, 1) //calculate the contribution of u's LightChild's SubTree
    update the ans of u
    big[BigChild] = 0
    if keep == 0 
        update(u, fa, -1) //remove the contributino of u's SubTree
        
update(u, fa, val)
    calculate u's information
    update(v : (u, v) and !big[v], u, val)
    

先递归计算轻儿子子树,递归结束时消除他们的贡献
再递归计算重儿子子树,保留他的贡献
再计算当前子树中所有轻子树的贡献
更新答案
如果当前子树是父节点的轻子树,消除当前子树的贡献


复杂度分析

显然只有遇到轻边才会把自己的信息合并到父节点
树链剖分后每个点到根的路径上有logn条轻边和lgon条重链
一个点的信息只会向上合并logn
如果一个点的信息修改是O(1)的,那么总复杂度就是O(nlogn)
从这里我们可以发现和对dfs序使用莫队有异曲同工之妙,莫队也要求修改的复杂度很低


应用

  1. 优秀的dfs序莫队替代品,复杂度nlogn
  2. 结合点分治的思想可以做一些有根树上的路径统计问题

模板题

CF600E. Lomsat gelral
题意:询问每颗子树中出现次数最多的颜色们编号和

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define pii pair<int, ll>
#define MP make_pair 
#define fir first
#define sec second
const int N=1e5+5;
int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}

int n, a[N];
struct edge{int v, ne;}e[N<<1];
int cnt, h[N];
inline void ins(int u, int v) {
	e[++cnt]=(edge){v, h[u]}; h[u]=cnt;
	e[++cnt]=(edge){u, h[v]}; h[v]=cnt;
}
int size[N], mx[N], big[N];
void dfs(int u, int fa) {
	size[u]=1;
	for(int i=h[u];i;i=e[i].ne) 
		if(e[i].v != fa) {
			dfs(e[i].v, u);
			size[u] += size[e[i].v];
			if(size[e[i].v] > size[mx[u]]) mx[u] = e[i].v;
		}
}

int cou[N], Max; ll ans[N];
pii f[N];
void update(int u, int fa, int val) {
	int &c = cou[a[u]];
	f[c].fir --; f[c].sec -= a[u];
	c += val;
	f[c].fir ++; f[c].sec += a[u];
	if(val==1) Max = max(Max, c);
	else if(!f[Max].fir) Max--;

	for(int i=h[u];i;i=e[i].ne) 
		if(e[i].v != fa && !big[e[i].v]) update(e[i].v, u, val);
}

void dfs(int u, int fa, int keep) {
	for(int i=h[u];i;i=e[i].ne) 
		if(e[i].v != fa && e[i].v != mx[u]) dfs(e[i].v, u, 0);
	if(mx[u]) dfs(mx[u], u, 1), big[mx[u]] = 1;
	update(u, fa, 1);
	ans[u] = f[Max].sec;
	big[mx[u]] = 0;
	if(!keep) update(u, fa, -1);
}
int main() {
	//freopen("in","r",stdin);
	n=read();
	for(int i=1; i<=n; i++) a[i]=read();
	for(int i=1; i<n; i++) ins(read(), read());
	dfs(1, 0);
	dfs(1, 0, 1);
	for(int i=1; i<=n; i++) printf("%I64d ",ans[i]);
}


posted @   Candy?  阅读(4840)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示