树链剖分(知识点整理)

news2025/1/24 22:40:14

思路来源

https://www.tuicool.com/articles/ee2QZf6

spoj375(树链剖分)-CSDN博客

概念

直接扒过来了,懒得写了……

显然轻子树比重子树小,就少于父亲的一半,

然后性质2的证明就是基于此的……

因为重链是间断的,所以两条重链夹着一条轻边,

然后若重链x条,必夹着(x-1)条轻边,由x-1为log级则重链log级

心得

及时把板子总结好到时候就能改吧改吧应对各种情况……

dfs1就是树形dp的基础操作,把要处理的都处理好

par[]:当前点的父亲
dep[]:当前点的深度(以根为0) 
siz[]:当前点的子树大小(包括自己)
son[]:当前点的重儿子(siz最大的那个儿子)
id[]:dfs序时间戳 
arc[]:id[]的映射,如id[u]=v,则arc[v]=u 
top[]:某一条链最靠近树根的结点 即边最靠上的那个端点 
tree[]:按照dfs序建的线段树 重链映射到连续区间 轻链映射到一个点 
cnt:edge的标号;num:时间戳的标号

dfs2的时候处理dfs序,优先搜索重儿子,这样重儿子就会堆到一起形成一个连续的区间

然后剩下的就是处理dfs序的线性序列,线段树操作,很常见的

询问u到v的时候,每次点向该链的top上走,直到两条链的top相同,即两个点走到一条链上,最后特殊处理这条链

其实询问LCA、最大、求和的操作也都是基于此,由于链的条数是log,

对重链求和的操作是log,对轻边求和的操作是1,所以每次查询是O(logn*logn)的

嗯这么总结过了以后应该不会再忘了叭

经典的操作:

①询问u到v这条链上的最大值最小值

②询问u到v这条链上的权值之和

③询问u到v这条链上的第k大值(树剖+主席树)

儿子以父节点为pre来建树,这样每个儿子对应的树就是到根节点的这条链,

询问u->v这条链内的权值的时候,u+v-lca(u,v)-par[lca(u,v)],传参查kth的时候传进去四棵树的参数就可以了

画一下图就搞出来了,lca(u,v)到根都被记了两遍,剩下的部分都被记了一遍

而lca(u,v)是需要一遍的,剩下的par[lca(u,v)]是需要被记零遍的,对应减掉就可以了

例题

bzoj2588 Spoj 10628. Count on a tree(主席树+树剖)

题解【主席树】bzoj2588 Spoj 10628. Count on a tree - AutSky_JadeK - 博客园

代码1(根据点权dfs序建树)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
struct edge
{
	int to,nex,w;
}e[maxn*2];  
struct node
{
	int max;
	ll sum;
}tree[maxn*5];
//par[]:当前点的父亲
//dep[]:当前点的深度(以根为0) 
//siz[]:当前点的子树大小(包括自己)
//son[]:当前点的重儿子(siz最大的那个儿子)
//id[]:dfs序时间戳 
//arc[]:id[]的映射,如id[u]=v,则arc[v]=u 
//top[]:某一条链最靠近树根的结点 即边最靠上的那个端点 
//tree[]:按照dfs序建的线段树 重链映射到连续区间 轻链映射到一个点 
//cnt:edge的标号;num:时间戳的标号
int par[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn];
int head[maxn],cnt,a[maxn];
int id[maxn],arc[maxn],num;
int n,q;
char op[10];
void add(int u,int v,int w)
{
	e[cnt].to=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt++;
}
void init()
{
	//memset(par,0,sizeof(par)); 由于dfs1的时候会覆盖 不需要 
	//memset(dep,0,sizeof(dep)); 由于后续dep[v]=dep[u]+1 只需更新根节点 
	//memset(siz,0,sizeof(siz)) 由于siz[u]=1覆盖 不需要 
	//memset(tree,0,sizeof(tree));
	//memset(top,0,sizeof(top)); 后续都是根据根节点更新的 
	//memset(id,0,sizeof(id)); num=0了什么都好说 
	dep[1]=0;top[1]=1;
	memset(son,0,sizeof(son));
	memset(head,-1,sizeof(head));
	cnt=num=0; 
	
}
void dfs1(int u)
{
	siz[u]=1;
	for(int i=head[u];~i;i=e[i].nex)
	{
		int v=e[i].to;
		if(v!=par[u])
		{
			par[v]=u;
			dep[v]=dep[u]+1;
			dfs1(v);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])son[u]=v;
		}
	}
}
void dfs2(int u)
{
	id[u]=++num;//dfs序 时间戳 id[i]即在线段树的第i位 想象给一颗dfs树标号 
	arc[id[u]]=u;//arc[i] 第i位的值对应的原节点 
	if(son[u])//优先遍历重儿子 保证重链dfs序相邻 
	{
		top[son[u]]=top[u];//重链上的根一定和父相同
		dfs2(son[u]); 
	} 
	for(int i=head[u];~i;i=e[i].nex)
	{
		int v=e[i].to;
		if(v!=par[u]&&v!=son[u])//非父 轻儿子
		{
			top[v]=v;//轻链以自己为根
			dfs2(v); 
		} 
	}
}
void pushup(int p)
{
	tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
	tree[p].max=max(tree[p<<1].max,tree[p<<1|1].max);
} 
void build(int p,int l,int r)
{
	if(l==r)
	{
		tree[p].sum=tree[p].max=a[arc[l]];
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(p);
} 
void update(int p,int l,int r,int pos,int v)//单点修改 
{
	if(l==r)
	{
		tree[p].sum+=v;
		tree[p].max+=v;
		return; 
	}
	int mid=(l+r)>>1;
	if(pos<=mid)update(p<<1,l,mid,pos,v);
	else update(p<<1|1,mid+1,r,pos,v);
	pushup(p);
}
ll asksum(int p,int l,int r,int ql,int qr)//区间和 
{
     if(ql<=l&&r<=qr)return tree[p].sum;
	 ll res=0;
	 int mid=(l+r)>>1;
	 if(ql<=mid)res+=asksum(p<<1,l,mid,ql,qr);
	 if(qr>mid)res+=asksum(p<<1|1,mid+1,r,ql,qr);
	 return res;
}
ll askmax(int p,int l,int r,int ql,int qr)//区间最值 
{
	 if(ql<=l&&r<=qr)return tree[p].max;
	 ll res=-1e18;
	 int mid=(l+r)>>1;
	 if(ql<=mid)res=max(res,askmax(p<<1,l,mid,ql,qr));
	 if(qr>mid)res=max(res,askmax(p<<1|1,mid+1,r,ql,qr));
	 return res;
}
int lca(int u,int v)//重链log 轻链log 故O(log)在线查询LCA 
{
	//保证u的那条链的top一直在下 
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=par[top[u]];//从top走向它的父亲 
	}
	//此时top[u]==top[v] 初始状况或未swap 判一下 返回靠上的 
	if(dep[u]>dep[v])swap(u,v); 
	return u; 
}
ll findsum(int u,int v)//u->v链之和 按轻重链找 
{
	ll ans=0;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		ans+=asksum(1,1,n,id[top[u]],id[u]);//重链区间和或轻链单点和 
		u=par[top[u]]; 
	}
	//u、v现在top相同 在一条链上 
	if(dep[u]>dep[v])ans+=asksum(1,1,n,id[v],id[u]);
	else ans+=asksum(1,1,n,id[u],id[v]);
	return ans; 
} 
ll findmax(int u,int v)//u->v链上的最大值 按轻重链找 
{
	ll ans=-1e18;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		ans=max(ans,askmax(1,1,n,id[top[u]],id[u]));//重链区间和或轻链单点和 
		u=par[top[u]]; 
	}
	//u、v现在top相同 在一条链上 
	if(dep[u]>dep[v])ans=max(ans,asksum(1,1,n,id[v],id[u]));
	else ans=max(ans,asksum(1,1,n,id[u],id[v]));
	return ans; 
} 
int main() 
{
	//freopen("test.in","r",stdin);
	//freopen("test.out","w",stdout); 
	int T;
	scanf("%d",&T);
	while(T--)
	{ 
	 scanf("%d",&n);
	 init();
	 for(int i=1;i<n;++i)//树 n-1条边 边权
	 {
	 	int u,v,w;
	 	scanf("%d%d%d",&u,&v,&w);
	 	add(u,v,w);
	 	add(v,u,w);
	 }
	 for(int i=1;i<=n;++i)
	 scanf("%d",&a[i]);//点上有点权的情况 
	 dfs1(1);
	 dfs2(1);
	 build(1,1,n);
	 /*后续操作*/
    }
	return 0;
}

代码2(根据边权dfs序建树)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1301505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从零开始搭建企业管理系统(四):集成 Knife4j

集成 Knife4j 前言Knife4j是什么集成 Knife4j引入 pom 依赖添加基础配置启动程序测试完善文档信息编写配置类修改 UserController修改 UserEntity修改 BaseEntity 文档效果图swagger 界面knife4j 界面 前言 前面一小节我们使用postman来进行接口的调试&#xff0c;如果接口一多…

服务器GPU占用,kill -9 PID 用不了,解决办法

PID&#xff08;progress ID 进程ID&#xff09; 上图为占用情况&#xff0c;使用下面的指令都不管用 kill -9 PID kill -15 PID # 加入sudo 还是不行 # 等等网上的 chatgpt 提供的其他办法&#xff0c;一圈试了下来还是不管用最后解决办法 首先用下面的指令查看进程的树结构…

【LeetCode刷题-树】-- 99.恢复二叉树

99.恢复二叉树 方法&#xff1a; 对二叉搜索树进行中序遍历得到值序列不满足的位置找到对应被错误交换的节点记为x和y交换x和y两个节点 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* Tre…

2024 年顶级的 Android 系统修复软件与方法

您是否正在寻找可以修复 PC 上 Android 操作系统的工具&#xff1f;这是我们精选的最好的 Android 系统修复软件&#xff01; Android 是世界著名的智能手机操作系统。全世界有数百万人使用这个操作系统&#xff0c;这使得它安全可靠。然而&#xff0c;这仍然不能使它完美无缺…

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-2稳定性分析Stability

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-2稳定性分析Stability 0. 序言1. 稳定的分类2. 稳定的对象3. 稳定的系统4. 系统稳定性的讨论5. 补充内容——Transfer Function(传递函数) - nonzero Initial Condition(非零初始…

深度学习——第4.2章 深度学习的数学基础

第4章 深度学习的数学基础 目录 4.6 矩阵 4.6 矩阵 下一章开始&#xff0c;我们就会用到矩阵。借助矩阵&#xff0c;可以用一个式子表示大量的联立方程式&#xff0c;特别方便。此外&#xff0c;使用矩阵或向量表示&#xff0c;也会更有助于我们直观理解方程式。 图4-13 标…

spring-cloud-starter-openfeign的maven引入方式引发的故障,分析其原理

一、背景 OpenFeign是一种声明式的REST客户端&#xff0c;openfeign是开发常用的对外提供服务&#xff0c;以及调用外部提供的openfeign接口的工具类&#xff0c;基于Java的HTTP客户端库&#xff0c;用于简化HTTP请求和响应的处理。OpenFeign支持多种编解码器&#xff0c;包括…

人工智能在安全领域的应用

ChatGPT 等 AI 应用在网络安全领域的应用效果明显&#xff0c;其自动编程能力、分析能力及自身集成的知识库能够帮助网络安全从业者提升工作效率&#xff0c;改进组织的网络安全计划。 &#xff08;一&#xff09;代码生成与检测能力 可用于开发漏洞挖掘工具。如目前可以利用…

【sgAutocomplete】自定义组件:基于elementUI的el-autocomplete组件开发的自动补全下拉框组件(带输入建议的自动补全输入框)

特性&#xff1a; 1、支持本地保存选中过的记录 2、支持动态接口获取匹配下拉框内容 3、可以指定对应的显示label和字段组件key 4、自动生成速记符字段&#xff08;包含声母和全拼两种类型&#xff09;&#xff0c;增强搜索匹配效率 sgAutocomplete源码 <template><!…

mjpg-streamer配置其它端口访问视频

环境 树莓派4B ubuntu 20.04 U口摄像头 确认摄像头可访问 lsusb查看 在dev下可查看到video* sudo mplayer tv://可打开摄像头并访问到视频 下载mjpg-streamer并编译安装 在github下载zip包&#xff0c;下载的源码&#xff0c;需要编译安装 unzip解压 cd mjpg-streamer/mjp…

win11 powershell conda 激活环境后不显示环境名称

win11 powershell conda 激活环境后不显示环境名称 问题现象解决方法 问题现象 安装 Anaconda 后在 powershell 中激活环境后&#xff0c;命令行前面不显示环境名称 解决方法 在 powershell 中执行 conda init 重新打开 poweshell 出现以下问题&#xff0c;请参考 win11 p…

C语言-每日刷题练习

[蓝桥杯 2013 省 B] 翻硬币 题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面&#xff0c;用 o 表示反面&#xff08;是小写字母&#xff0c;不是零&#xff09;&#xff0c;比如可能情形是 **oo***oooo&#xff0c;如果…

基于hadoop下的spark安装

目录 简介 安装准备 spark安装 配置文件配置 简介 Spark主要⽤于⼤数据的并⾏计算&#xff0c;⽽Hadoop在企业主要⽤于⼤数据的存储&#xff08;⽐如HDFS、Hive和HBase 等&#xff09;&#xff0c;以及资源调度&#xff08;Yarn&#xff09;。但是也有很多公司也在使⽤MR2进…

坚鹏:中国邮政储蓄银行数字化转型战略、方法与案例培训

中国邮政储蓄银行拥有优良的资产质量和显著的成长潜力&#xff0c;是中国领先的大型零售银行。2016年9月在香港联交所挂牌上市&#xff0c;2019年12月在上交所挂牌上市。中国邮政储蓄银行拥有近4万个营业网点&#xff0c;服务个人客户超6.5亿户。2022年&#xff0c;在《银行家》…

关于大模型ChatGLM3-6B在CPU下运行

最近在调研市场上语言大模型&#xff0c;为公司的产品上虚拟人的推出做准备。各厂提供语言模型都很丰富&#xff0c;使用上也很方便&#xff0c;有API接口可以调用。但唯一的不足&#xff0c;对于提供给百万用户使用的产品&#xff0c;相比价格都比较贵。所以对ChatGLM3-6B的使…

仿照MyBatis手写一个持久层框架学习

首先数据准备&#xff0c;创建MySQL数据库mybatis&#xff0c;创建表并插入数据。 DROP TABLE IF EXISTS user_t; CREATE TABLE user_t ( id INT PRIMARY KEY, username VARCHAR ( 128 ) ); INSERT INTO user_t VALUES(1,Tom); INSERT INTO user_t VALUES(2,Jerry);JDBC API允…

2024 年最值得推荐的 7 个 Vue3 组件库

你好&#xff0c;我是 Kagol。 Vue 是一款易学易用&#xff0c;性能出色&#xff0c;适用场景丰富的渐进式 JavaScript 框架&#xff0c;深受广大开发者的喜爱&#xff0c;Vue3 更是推出了 Composition API&#xff0c;让逻辑复用更友好。 马上就到 2024 年了&#xff0c;如果…

html通过CDN引入Vue使用Vuex以及Computed、Watch监听

html通过CDN引入Vue使用Vuex以及Computed、Watch监听 近期遇到个需求&#xff0c;就是需要在.net MVC的项目中&#xff0c;对已有的项目的首页进行优化&#xff0c;也就是写原生html和js。但是咱是一个写前端的&#xff0c;写html还可以&#xff0c;.net的话&#xff0c;开发也…

dell r720远程网络安装ubuntu20.04(无U盘)

登陆后界面&#xff0c;在主界面上&#xff0c;我们就可以看到各个硬件组件的状态。在快速启动任务栏中&#xff0c;可以对系统电源进行操作&#xff0c;如开机、关机等。安装操作系统&#xff0c;在虚拟控制台预览处点击>启动 按照浏览器出现的提示确定安装控件等&#x…

西南科技大学数字电子技术实验四(基本触发器逻辑功能测试及FPGA的实现)FPGA部分

实验目的1、掌握基本RS触发器、集成D触发器和JK触发器的逻辑功能及测试方法。 2、熟悉D触发器和JK触发器的触发方法。 3、熟悉用JK和D触发器构成其他功能触发器的方法。 4、学会用FPGA实现本实验内容。 实验原理1、D触发器 Qn+1 = D 2、JK触发器 3、RS触发器 程序清单(每…