C++树形结构(3 树的中心、重心)

目录

一.树的中心:

1.树的概念:

2.树的性质:

性质1:

性质2:

3.树的中心求解:

4.例题:

二.树的重心:

1.基础概念:

2.求解方法:

3.例题:

4.重心的性质:

性质1:

性质2:

性质3:

性质4:

性质5:

5.例题:

①子树的重心

②唯一的重心

③会议


一.树的中心:

除了直径的端点还有一个点我们完成题目时经常会用到,这就是树的中心。

1.树的概念:

以树的中心为根时,从该根到每个叶子节点的最长路径最短,使得路径和平衡。实际应用:在若干村庄中(树形结构)修一个小学,使得所有村庄到学校的最大距离最小,小学应该修在什么位置?

2.树的性质:

性质1:

树的中心一定在直径上,且趋向于中点位置

性质2:

树的中心可以有一个(单中心),也可以有两个(双中心)

证明:引理性质2,若树的中心p不在直径st上,st上有一点q与直径联通。中心点能到的最远距离为:max(qs,qt)+pq,若要使得该值最小,pq应当为0,因此p在直径上。同时为了让max(qs,qt)更小,树的中心要在直径中点处。

3.树的中心求解:

我们现在已经知道求解任意一点到两端点的距离,即根据性质2可很轻松得到每个点能到的最长路径。求出每个点后的路径后,一次遍历便可知树的中心点。

4.例题:

题目描述:

给定一棵树,树中包含 n个结点(编号1~n)和 n−1 条无向边,每条边都有一个正权值。请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。输出该节点以及最近距离。(若存在多个点的距离相同,则输出编号较小的一个)

题目分析:

有data数组表述每个点出发的最大距离,因此我们在第2次和第3次dfs的过程中,与dis数组比较即可。并且在第3次dfs时,把最小距离求出来。

正确代码:

#include<bits/stdc++.h>
using namespace std;
int n,data[100001],dl[100001],maxn,maxn2,minn=2e9,tmp,pl;
vector<int> v[10001];
vector<int> w[10001];
void dfs(int x,int fa,int cnt) {for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fa) continue;data[y]=data[x]+w[x][i];if(maxn<data[y]) pl=y,maxn=data[y];if(cnt==3) {maxn2=max(data[y],dl[y]);if(maxn2<minn) minn=maxn2,tmp=y;}dfs(y,x,cnt);}
}
int main() {cin>>n;for(int i=1; i<=n-1; i++) {int x,y,z;cin>>x>>y>>z;v[x].push_back(y);v[y].push_back(x);w[x].push_back(z);w[y].push_back(z);}dfs(1,0,1);maxn=0;memset(data,0,sizeof data);dfs(pl,0,2);for(int i=1; i<=n; i++)	dl[i]=data[i];memset(data,0,sizeof data);maxn=0;dfs(pl,0,3);cout<<tmp<<" "<<minn;return 0;
}

二.树的重心:

1.基础概念:

使得最大子树大小最小。那么这个点叫就被叫做树的重心

在线性的序列[1,n]中,我们在考虑用分治思想处理问题时,需对问题进行划分。在划分问题时若要更加均匀,我们选择中点mid可以更加高效。这样得到[1,mid],[mid+1,n]两个子序列,因为子序列中元素的个数<=n/2(向上取整),这样可以把问题复杂度优化到O(logn)

2.求解方法:

显然,要求树的重心,我们可以枚举出每个点为断点时,所产生的最大子树大小。某断点求当前最大子树大小的方法:对该点进行dfs,找到以i为根节点的子树的大小记录到sz[i]中,接着在该点的儿子中找si最大的一个。复杂度为O(n2)

3.例题:

题目描述:

给定一棵树,树中包含 n(n<=1e5)个结点(编号1~n)和 n−1 条无向边,找出树的重心若重心不止一个,则输出编号较小的),以及当前重心下的最大子树大小。

题目分析:

这是一道关于重心的基础题,仅需用重心的求解方法,再用之前讲的求最大子树的方法就可以了。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
vector<int> v[N];
int d[N],minn=N,n;//d数组记录当前节点子树的大小
int res,id;//id记录重心,minn为重心下最大子树的大小
void dfs(int x,int fa) {d[x]=1;int res=0;//开始找以x为根的最大子树for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fa) continue;dfs(y,x);d[x]+=d[y];res=max(d[y],res);//打擂台找几个子树中的最大值}res=max(res,n-d[x]);//除最大字数为,剩下的子树if(minn>res) minn=res,id=x;
}
int main() {cin>>n;for(int i=1; i<n; i++) {int x,y;cin>>x>>y;v[x].push_back(y);v[y].push_back(x);}dfs(n,0);cout<<id<<" "<<minn;return 0;
}

4.重心的性质:

设mss(u)表示以u为重心的最大子树,s0(u)表示以u为根的子树大小,su(v)表示以u为根的的子树v大小。

性质1:

重心点的最大子树大小不大于整棵树大小的一半。

证明:

设u为重心,v为u的最大子树。可以得出:s0[u]-su[v]>=su[v] ,即 su[v]<=s0[u]/2在整颗树中,存在s0[u]=n,所以su[v]<=n/2得证以某点为根,最大子树大小不超过n/2的都是树的重心

常用推导:

Ⅰ.以某点为根,最大子树大小不超过n/2的一定是树的重心。

Ⅱ.以root为根的有根树中,树的重心一定在其最大的一颗子树内。具体来讲,假设y为root的最大子树的儿子,那么重心一定在tp[y]->root的这一条链中(tp[y]表示子树y的重心)。

性质2:

非空树有且仅有1-2个重心。当有两个重心时,树定有偶数个节点,且两个重心相邻。

证明:

假设u、v为树上两个重心,u,v分别为对方最长链上的点。此时:mss[u]=mss[v]又设k为两个重心之间存在的点数。由mss[u]=su[v]+k,mss[v]=sv[u]+k,推出sv[u]=su[v]。在k个点中选择中点p,此时,mss[p]=max(su[v]+k/2,sv[u]+k/2) >=su[v]+k,当且仅当k=0时,不等式成立。重心u、v之间必不可能有点。所以若有两个重心,则重心必然相邻。

性质3:

树中所有点到重心的距离和最小,反过来距离和最小的点一定是重心。

证明:

当前重心为u。mss[u]=su[v]。假设重心从u移动到v,mss[v]=sv[u],可得1类节点到重心的距离加1,2类节点到重心的距离减少1,因此当增加部分sv[u] 小于 减少部分 sv[u]时,距离和减少所以当su[v]>sv[u]时,重心移动,得到mss更小。反之若当前mss已经最小,则无法再产生一个更小距离和。

性质4:

往树上增加或减少一个叶子,如果原节点数是奇数,那么重心可能增加一个,原重心仍是重心;如果原点数是偶数,重心可能减少一个,另一个重心仍是重心。节

性质5:

把两棵树通过一条边相连得到一棵新的树,则新的重心在较大的一棵树一侧的连接点与原重心之间的简单路径上。如果两棵树大小一样,则重心就是两个连接点。

5.例题:

①子树的重心

题目描述:

输入一棵树,判断每一棵子树的重心是哪一个节点。第一行输入n,q。n表示树的节点个数,q表示询问次数。第二行n-1个数,分别表示从节点2开始,各节点的父亲节点。后面q行,每行一个数x,表示询问当前以x为根的子树中,树的重心位置。(n,q<=3e5)

题目分析:

本题若对每一次询问都查询一遍子树的重心,那么复杂度为O(nq)。在我们求一颗树T的重心时,根据推导2知道,重心一定在最大子树的重心到该树T的根这一条链上。所以我们如果知道最大子树的重心,此时就可以遍历这一条链上的点,根据推导1,只要该点满足其最大子树大小不超过n/2,那么一定是重心。所以我们可以dfs下去,先求出小的子树重心,回溯时再把当前的重心进行记录即可。复杂度O(n+q)

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
vector<int> v[N];
int d[N],minn=N,res,id[N],n,t[N],s[N],q;
void dfs(int x) {d[x]=1;int res=0;for(int i=0; i<v[x].size(); i++) {int y=v[x][i];dfs(y);d[x]+=d[y];if(d[y]>d[id[x]]) id[x]=y;//找子树最大的“儿子”}if(id[x]==0) {//叶子节点的重心就是自己t[x]=x;return;}t[x]=t[id[x]];//从“儿子”的重心调到自身,满足条件且靠近xwhile(d[t[x]]*2<d[x]) t[x]=s[t[x]];//向上找重心
}
int main() {cin>>n>>q;for(int i=2; i<=n; i++) {int x;cin>>x;s[i]=x;v[x].push_back(i);}dfs(1);//有根树,往下进行dfsfor(int i=1; i<=q; i++) {int y;cin>>y;cout<<t[y]<<endl;}return 0;
}
//d[i]:表示以i为根的子树大小
//s[i]:表示节点i的父亲节点
//id[i]:表示以i为根的有根树的最大子树
//t[i]:表示以i为根的有根数的重心

②唯一的重心

题目描述:

给定一棵节点数为 n(n<=3e5) 的树 , 删一条边然后加上一条边 , 使得该树的重心唯一 。(删掉的边和加上的边可以是同一条)输出删边与加边信息,本题多测。

题目分析:

若存在一个重心,删边与加边都可以是同一条边。若不止一个重心,引理性质2,最多存在两个重心,且重心直接相连。假设两重心分别为idx,idy。要保证只留下一个重心,那就应当对某个重心子树idx进行修改,删除其叶子节点的一条边,且将该叶子节点直接连到另一个重心idy上即可。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,minn=N,z1,z2;//z1、z2两个重心
int u,sz[N],f[N];//u是断开的叶子节点
vector<int>v[N];
void dfs(int x,int fa) {sz[x]=1,f[x]=fa;int res=0;for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fa) continue;sz[x]+=sz[y];res=max(res,sz[y]);}res=max(res,n-sz[x]);if(minn>res) z1=x,z2=0,minn=res;else if(minn==res) z2=x;
}
void dfs1(int x,int fa) {if(v[x].size()==1) u=x;for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fa) continue;dfs1(y,x);}
}
int main() {int t;cin>>t;while(t--) {cin>>n;for(int i=1; i<n; i++) {int x,y;cin>>x>>y;v[x].push_back(y);v[y].push_back(x);}dfs(1,0);if(z2==0) {//只有一个重心,就直接输出cout<<"1 "<<v[1][0]<<endl<<"1 "<<v[1][0]<<endl;continue;}if(f[z1]!=z2) swap(z1,z2);//保证只有一个重心在子树上遍历dfs1(z1,z2);cout<<u<<" "<<f[u]<<endl<<u<<" "<<z2<<endl;for(int i=1; i<=n; i++) {f[i]=0,v[i].clear();}minn=N,z1=z2=0;}return 0;
}

③会议

题目描述:

有一个村庄居住着 n 个村民,有 n−1 条路径使得这 n 个村民的家联通,每条路径的长度都为 1。现在村长希望在某个村民家中召开一场会议,村长希望所有村民到会议地点的距离之和最小,那么村长应该要把会议地点设置在哪个村民的家中,并且这个距离总和最小是多少?若有多个节点都满足条件,则选择节点编号最小的那个点。

题目分析:

求所有点到某点的距离和,根据重心性质3,显然是到重心最小,因此求出重心,在进行距离和统计即可。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n,minn=N,idx,idy,sum,u,sz[N];//sz数组记录当前节点的子树大小
vector<int>v[N];
void dfs(int x,int fat) {sz[x]=1;int res=0;for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fat) continue;dfs(y,x);sz[x]+=sz[y];res=max(sz[y],res);}res=max(res,n-sz[x]);if(minn>res) idx=x,minn=res;else if(minn==res&&idx>x) idx=x;
}
void dfs1(int x,int fat,int num) {sum+=num;for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fat) continue;dfs1(y,x,num+1);}
}
int main() {cin>>n;for(int i=1; i<n; i++) {int x,y;cin>>x>>y;v[x].push_back(y);v[y].push_back(x);}dfs(1,0);//找重心dfs1(idx,0,0);//统计距离和cout<<idx<<" "<<sum;return 0;
}

树形结构(1 基础):https://blog.csdn.net/Archie28/article/details/140532542

树形结构(2 树的直径):https://blog.csdn.net/Archie28/article/details/140532713

树形结构(总):https://blog.csdn.net/Archie28/article/details/140504428

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

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

相关文章

毕业/期刊论文发表必备:YOLOv5 / v7 / v8 /v10算法网络结构图【文末提供原型文件下载地址】

前言:Hello大家好,我是小哥谈。同学们在写YOLO算法相关毕业论文/期刊论文的时候,不可避免的会用到相关版本的网络结构图,曾有很多小伙伴私信我索要原型文件,本文就给大家提供YOLOv5/v7/v8/v10版本算法网络结构图及原型文件下载地址。🌈 目录 🚀1.网络结构图 �…

C#中implicit 关键字的使用:隐式转换操作符

在 C# 中,implicit 关键字用于定义隐式转换操作符。隐式转换操作符允许自动将一种数据类型转换为另一种类型,而无需显式地调用转换方法或进行类型转换。下面将详细介绍 implicit 关键字的定义和使用。 1. 隐式转换操作符 定义 隐式转换操作符可以定义在一个类或结构体中,…

SpringBoot源码深度解析

今天&#xff0c;聊聊SpringBoot的源码&#xff0c;本博客聊的版本为v2.0.3.RELEASE。目前SpringBoot的最新版为v3.3.2&#xff0c;可能目前有些公司使用的SpringBoot版本高于我这个版本。但是没关系&#xff0c;因为版本越新&#xff0c;新增的功能越多&#xff0c;反而对Spri…

简单介绍什么是投影仪及投影仪的工作原理

投影仪的定义 投影仪&#xff0c;又称投影机&#xff0c;是一种可以将图像或视频信号转换为光信号&#xff0c;并通过光学系统放大投射到屏幕或墙壁上的设备。它可以通过不同的接口与计算机、VCD、DVD、BD、游戏机、DV等相连接&#xff0c;播放相应的视频信号。投影仪广泛应用…

如何在 Android 应用中通过 URL 获取文件扩展名

如何在 Android 应用中通过 URL 获取文件扩展名 ​ 在 Android 应用开发中&#xff0c;获取文件的扩展名是一个常见的需求。无论是用于文件管理、下载处理还是内容预览&#xff0c;准确地获取文件扩展名都至关重要。本文将详细介绍如何实现一个 FileExtensionFetcher 类&#…

2024-07-19 Unity插件 Odin Inspector9 —— Validation Attributes

文章目录 1 说明2 验证特性2.1 AssetsOnly / SceneObjectsOnly2.2 ChildGameObjectsOnly2.3 DisallowModificationsIn2.4 FilePath2.5 FolderPath2.6 MaxValue / MinValue2.7 MinMaxSlider2.8 PropertyRange2.9 Required2.10 RequiredIn2.11 RequiredListLength2.12 ValidateIn…

Python数据风险案例54——人工智能热门概念股爬虫分析其价值(三因子模型)

案例背景 人工智能概念如火如荼的夏天&#xff0c;在这个2024年&#xff0c;我觉得需要提早布局一下这个概念。所以我们找一下A股里面人们的人工智能概念股&#xff0c;然后分析他们的数据应用三因子模型&#xff0c;也就是最经典的资本资产定价模型的衍生版去研究他们各自的投…

51 单片机[8]:串口通信

一、目标 单片机通过串口向电脑发送数据&#xff08;数字递增&#xff09;电脑通过串口控制单片机上的LED 二、基本概念 1. 串口 串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信。 单片机的串口可以…

Visio绘制的relu激活函数结构图,可导出高清图片,可修改,无水印。

Visio绘制的relu激活函数结构图,可导出高清图片&#xff0c;可修改&#xff0c;无水印。 方便用于小论文写作&#xff0c;方便用于毕业设计。 Visio版本为2021版&#xff0c;可用更高版本打开。 下载地址&#xff1a;地址 图片展示&#xff1a;

【Linux Commands】命令分类整理-命令搜索引擎-速查手册

Linux命令速查网站&#xff1a;https://linux.embeddev.asia 目前已经搜集了600条命令,对每一条命令都有详细的介绍。 本文展示的命令不全是安装系统时自带的。 Linux命令基于Unix哲学&#xff0c;旨在创建简单、短小、可组合的工具。每个命令通常只执行一个任务&#xff0…

open3d:随机采样一致性分割平面

1、背景介绍 随机采样一致性算法&#xff08;RANSAC Random Sample Consensus&#xff09;是一种迭代的参数估计算法&#xff0c;主要用于从包含大量噪声数据的样本中估计模型参数。其核心思想是通过随机采样和模型验证来找到数据中最符合模型假设的点。因此&#xff0c;只要事…

TI 【ads131m02】DSP TMS320F280049C调试与学习笔记

ads131m02 调试与学习笔记 时序SPI 参考链接&#xff1a; ADS131M02_TI官网资料参考 ADS131M02—英文使用手册 ADS131M0x—参考代码 Example C Code ADS131M02 是一款 two 通道、同步采样、24 位、ΔΣ 模数转换器 (ADC)&#xff0c;具有宽动态范围、低功耗和电能测量特定功能…

你还在手动构建Python项目吗?PyBuilder让一切自动化!

在 Python 项目开发中&#xff0c;构建和管理项目是一项繁琐但必不可少的工作。你可能需要处理依赖项、运行测试、生成文档等。这时候&#xff0c;PyBuilder 出场了。它是一个强大的构建自动化工具&#xff0c;可以帮助你简化项目管理&#xff0c;让你更专注于编写代码。 什么…

学习SQL权限管理的基础知识

学习SQL权限管理的基础知识 一、前言1. 授予权限2. 刷新权限3. 撤销权限4. 注意事项 一、前言 在使用MySQL或其他关系型数据库时&#xff0c;管理用户权限是确保数据安全和访问控制的关键部分。本文将介绍如何使用SQL语句来管理MySQL数据库中的用户权限。 1. 授予权限 首先&…

白骑士的PyCharm教学基础篇 1.4 版本控制与集成

Git集成与基本操作 Git集成 配置Git 打开PyCharm&#xff0c;依次点击 “File” -> “Settings” -> “Version Control” -> “Git”&#xff1b;在 “Path to Git executable” 中&#xff0c;确保Git的路径正确。如果未安装Git&#xff0c;可以从Git官网下载并安…

PHP 安装指南

PHP 安装指南 PHP(Hypertext Preprocessor)是一种广泛使用的开源服务器端脚本语言,特别适用于网页开发。本指南将详细介绍如何在不同的操作系统上安装PHP,包括Windows、Linux和macOS。 Windows系统上的PHP安装 使用Windows安装程序 下载PHP安装程序:访问PHP官方网站(p…

Unity Meta Quest 开发:如何在每只手指上添加 Poke 交互

XR 开发社区&#xff1a; SpatialXR社区&#xff1a;完整课程、项目下载、项目孵化宣发、答疑、投融资、专属圈子 找到玩家物体 OVRCameraRig 下的子物体 HandInteractorsRight/Left&#xff08;分别管理左右手的 Interactor&#xff09;下的 HandPokeInteractor 子物体&#x…

JMX 反序列化漏洞

前言 前段时间看到普元 EOS Platform 爆了这个洞&#xff0c;Apache James&#xff0c;Kafka-UI 都爆了这几个洞&#xff0c;所以决定系统来学习一下这个漏洞点。 JMX 基础 JMX 前置知识 JMX&#xff08;Java Management Extensions&#xff0c;即 Java 管理扩展&#xff0…

程序的机器级表示(一)汇编,汇编格式和数据传输指令

系列文章 : 深入理解计算机系统笔记 文章目录 系列文章3 程序的机器级表示3.1 历史观点3.2 程序编码3.2.1 机器级代码3.2.2 代码示例3.2.3 关于格式的注解 3.3 数据格式3.4 访问信息3.4.1 操作数指示符3.4.2 数据传送指令3.4.3 数据传送示例3.4.4 压入和弹出栈数据 3 程序的机…

centos/rocky容器中安装xfce、xrdp记录

最近需要一台机器来测试rdp连接&#xff0c;使用容器linuxxfcexrdp来实现&#xff0c;在此记录下主要步骤 启动rockylinux容器&#xff08;其他linux发行版步骤应该相似&#xff09; docker run -it -p 33891:3389 rockylinux:9.3 bash容器内操作 # 省略替换软件源步骤 ...# …