首页 > 其他分享 >树上启发式合并学习笔记

树上启发式合并学习笔记

时间:2023-02-04 20:47:34浏览次数:50  
标签:遍历 儿子 int siz 笔记 son 启发式 树上 节点

省流:高级暴力。

首先明确几个概念:

  • 重儿子:一个节点体积最大的儿子节点

  • 轻儿子:除重儿子之外的儿子节点

树上启发式合并适用于一类无修改子树统计问题。

先看一道例题:CF600E Lomsat gelral

暴力很显然,对每个点遍历子树,统计答案,复杂度 \(O(n^2)\)。

而 DSU on tree 是这么做的:

从根节点开始遍历,遍历到一个点时:

  1. 先遍历其所有轻儿子,并清空它的统计数组,但保留答案(一定要分清区别)

  2. 如果不是叶子节点,遍历其重儿子,保留统计数组,保留答案。

  3. 再遍历轻儿子,将轻儿子信息加到重儿子的统计数组上,直接将答案累计到重儿子答案上,此时答案即为该节点答案。

  4. 清空轻儿子贡献

代码:

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define debug cout<<"DEBUG"<<endl;
#define pb push_back
#define pii pair<int,int>
#define vi vector<int>
#define imp map<int,int>
using namespace std;
const int N=100005;
int n,h[N],cnt,a[N],siz[N],son[N];
ll ans[N],sum,maxn,t[N];
struct node{
	int to,nxt;
}e[N<<1];
void add(int x,int y){
	e[++cnt].to=y;
	e[cnt].nxt=h[x];
	h[x]=cnt;
}
void dfs(int x,int fa){
	siz[x]=1;
	for(int i=h[x];i;i=e[i].nxt){
		int to=e[i].to;
		if(to!=fa){
			dfs(to,x);
			if(son[x]==0||siz[to]>siz[son[x]]){
				son[x]=to;
			}
			siz[x]+=siz[to];
		}
	}
}
void add(int x,int fa,int ds,int d){
	t[a[x]]+=d;
	if(t[a[x]]>maxn){
		maxn=t[a[x]];
		sum=0;
	}
	if(t[a[x]]==maxn){
		sum+=a[x];
	}
	for(int i=h[x];i;i=e[i].nxt){
		int to=e[i].to;
		if(to!=fa&&to!=ds){
			add(to,x,ds,d);
		}
	}
}
void solve(int x,int fa,bool d){
	for(int i=h[x];i;i=e[i].nxt){
		int to=e[i].to;
		if(to!=fa&&to!=son[x]){
			solve(to,x,0);
		}
	}
	if(son[x]){
		solve(son[x],x,1);
	}
	add(x,fa,son[x],1);
	ans[x]=sum;
	if(!d){
		add(x,fa,0,-1);//尤其是这里!注意重子树也要清空!因为整个都属于轻子树!
		sum=maxn=0;
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	dfs(1,1);
	solve(1,1,1);
	for(int i=1;i<=n;i++){
		cout<<ans[i]<<" ";
	}
	return 0;
}

证明一下复杂度:

先证明一个性质:对于每个点,从根节点到它的轻边条数小于 \(\log n\)。

我们设该节点大小为 \(siz\),轻边条数为 \(x\),因为轻儿子一定小于二分之一的父节点大小,所以可知 \(siz \leq \dfrac{n}{2^x}\)。

移项可得 \(siz \cdot 2^x\leq n\)

因为 \(siz\) 一定为正,可知 \(2^x\leq n\),即 \(x \leq \log n\)。

我们知道只有暴力统计轻边时一个点会被计算(去掉遍历的一次),由于轻边条数小于 \(\log n\),所以只会被重复统计 \(\log n\) 次,所以总复杂度为 \(O(n \log n)\)。

标签:遍历,儿子,int,siz,笔记,son,启发式,树上,节点
From: https://www.cnblogs.com/victoryang-not-found/p/17092334.html

相关文章

  • golang笔记
    手册网站:https://studygolang.com/pkgdocos.OpenFile("./app.log",os.O_CREATE|os.O_RDWR|os.O_APPEND,0644)app.log是文件名字,os.O_CREATE|os.O_RDWR|os.O_APPEND是......
  • GO语言的实战学习(猜谜游戏和在线词典)| 青训营笔记
    一.GO语言的实战学习1.1前言在上文我们急速学习了Go语言的入门,今天我们来学习一下Go语言的实战二.猜谜游戏1.导入依赖包:"math/rand",代码如下:import("fmt""ma......
  • Android笔记--内容提供者+Server端+Client端
    什么是内容提供者ContentProvider为App存取内部数据提供的统一的外部接口,让不同的应用之间得以实现数据共享ClientApp端用户输入数据的一端,或者说是用户读取到存储的数......
  • TMC5160步进电机驱动芯片开发使用笔记-1
    内容主要来自TMC5160数据手册,个人的理解简单做下笔记:TMC5160做为驱动芯片,单片机作为控制器,控制一个或多个5160;站在应用的角度去分析,不涉及过多电子方面;    TMC5......
  • 关于软件技术的一些笔记
    不同的角色:开发人员、运维团队、DevOps工程师开发团队和IT运维团队的技能要求和工作目标可能都不相同。开发人员希望给应用增加新功能,而运维团队的重点则是在应用发布......
  • 《深入理解Java虚拟机》第三章读书笔记(三)——经典垃圾回收器
    系列文章目录和关于我一丶概述上图展示了经典的垃圾回收器,其中Serial,ParNew,ParallelScavenge(途中的Parallel)作用在新生代SerialOldCMS,ParallelOld作用在老年......
  • 小程序实战项目笔记
    1.项目说明  本项目名称为叮当书店,为校内的一个书籍购物平台,为学生提供书籍购买、订单查询、心得分享等服务。  技术栈为uni-app+Vue+vuex。2.踩坑2.1vuex的......
  • SpringBoot 场景开发多面手成长手册 小册笔记
    整合RocketMQ在开始运行RocketMQ之前,我们先思考一个实际的场景。假设我们项目中有一个消息的生产者和消费者,它们连接到一个RocketMQ实例上,如下图所示。随着业务规......
  • 回溯法学习笔记
    回溯法学习笔记目录回溯法学习笔记1,什么是回溯法2,什么时候使用回溯法3,回溯法的套路4,例题1,什么是回溯法回溯法其实是一种使用递归的暴力搜索法2,什么时候使用回溯法在常......
  • 【随笔记】T507 ADC SGM58031 16BIT 4Channel 调试记录
    文章介绍本文主要描述在T507Android10Linux4.9平台下,调试SGM58031芯片的记录,实现单芯片实时采集外部四通道的电压数值。芯片介绍SGM58031是一款低功耗、16位......