树的直径&树的重心
树的直径
定义
那么树上最远的两个点,他们之间的距离,就被称之为树的直径。
树的直径的性质
1. 直径两端点一定是两个叶子节点。
2. 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的正确性 可以得出。
3. 对于两棵树,如果第一棵树直径两端点为(u,v),第二棵树直径两端点为 (x,y),用条边将两棵树连接,那么新树的直径一定是u,v,x,y中的两个点。
4. 对于一棵树,如果在一个点上接一个叶子节点,那么最多会改变直径的一个端 点。
5. 若一棵树存在多条直径,那么这些直径交于一点且交点是这些直径的中点。
树的直径的求法
解法1:树型DP
直接上代码:
void dp(int x,int fa){//f[i]表示以i为根节点的最长链
for(int i=head[x];i;i=e[i].next){
int y=e[i].v;
if(y==fa) continue;//防止死循环
dp(y,x);
ans=max(ans,f[x]+f[y]+e[i].w);//更新最长链+次长链的长度
f[x]=max(f[x],f[y]+e[i].w);//更新f[x]的长度
}
}
树形DP有一些难理解,但他可以解决负边权的问题
解法2:两次dfs/bfs
上代码:
void dfs(int x,int fa){//dis[i]表示以i的最长距离
if(dis[x]>ans){
ans=dis[x];//更新
p=x;//记录最远的点,因为要两次dfs才能确定直径
}
for(int i=head[x];i;i=e[i].next){
int y=e[i].v;
if(y==fa)continue;
dis[y]=dis[x]+e[i].w;//计算最长距离
dfs(y,x);
}
}
解法2运用了性质2,易理解,但不能处理负边权问题。
上一道例题:https://www.luogu.com.cn/problem/P3629
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k;
int a,b;
int p;
int idx=1;
int head[N];
int dis[N];
int pass[N];
int f[N];
int ans=0; struct node{
int v;
int next;
int w;
}e[N*2]; void add(int a,int b){
e[++idx].v=b;
e[idx].next=head[a];
e[idx].w=1;
head[a]=idx;
} void dfs(int x,int fa){
if(dis[x]>ans){
ans=dis[x];
p=x;
}
for(int i=head[x];i;i=e[i].next){
int y=e[i].v;
if(y==fa)continue;
dis[y]=dis[x]+e[i].w;
pass[y]=i;
dfs(y,x);
}
} void dp(int x,int fa){
for(int i=head[x];i;i=e[i].next){
int y=e[i].v;
if(y==fa) continue;
dp(y,x);
ans=max(ans,f[x]+f[y]+e[i].w);
f[x]=max(f[x],f[y]+e[i].w);
}
} int main(){
cin>>n>>k;
for(int i=1;i<n;i++){
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs(1,0);
memset(dis,0,sizeof(dis));
memset(pass,0,sizeof(pass));
ans=0;
dfs(p,0);
if(k==1){
cout<<2*(n-1)-ans+1;
return 0;
}
int l1=ans;
ans=0;
while(pass[p]){
e[pass[p]].w=-1;
e[pass[p]^1].w=-1;
p=e[pass[p]^1].v;
}
dp(1,0);
cout<<2*(n-1)-l1-ans+2;
return 0;
}
树的重心
定义
树的重心也叫树的质心。对于一棵 个节点的无根树,找到一个点,使得把树变成以该 点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块(一定是 树)的结点数最小,删去重心后,生成的多棵树尽可能平衡。
性质
1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心, 他们的距离和一样。
2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
4. 一棵树最多有两个重心,且相邻
思路
找到以i为根结点的最大子树大小,与现在最小最大子树相比较,不断更新找出重心
//minNode当前重心节点
//minBalance当前重心节点的最大子树节点个数
int d[maxn];
//d[i]表示以i为根的子树节点个数
void dfs(int u,int fa){
d[u]=1; //节点本身
int maxSub=0,size=tree[u].size(); //maxSub为节点u的最大子树节点个数
for(int i=0;i<size;i++){
int v=tree[u][i];
if(v!=fa){
dfs(v,u);
d[u]+=d[v];
maxSub=max(maxSub,d[v]);
}
}
maxSub=max(maxSub,n-d[u]);
if(maxSub<minBalance){
minNode=u;
minBalance=maxSub;
}
}
树的直径&树的重心的更多相关文章
- 【SDOI2011 第2轮 DAY1】消防 -[树的直径+树链剖分][解题报告]
[SDOI2011 第2轮 DAY1]消防 题面: SDOI2011 第2轮 DAY1]消防 时间限制 : 20000 MS 空间限制 : 565536 KB 问题描述 时限\(2s\) 某个国家有\ ...
- 树形DP 学习笔记(树形DP、树的直径、树的重心)
前言:寒假讲过树形DP,这次再复习一下. -------------- 基本的树形DP 实现形式 树形DP的主要实现形式是$dfs$.这是因为树的特殊结构决定的——只有确定了儿子,才能决定父亲.划分阶 ...
- D4 树的直径、重心以及基环树
第一题第二题鉴上我前几篇博客poj1985 poj1849:https://www.cnblogs.com/Tyouchie/p/10384379.html 第三题:数的重心:poj1655 来自sj ...
- Codeforces 1182D Complete Mirror 树的重心乱搞 / 树的直径 / 拓扑排序
题意:给你一颗树,问这颗树是否存在一个根,使得对于任意两点,如果它们到根的距离相同,那么它们的度必须相等. 思路1:树的重心乱搞 根据样例发现,树的重心可能是答案,所以我们可以先判断一下树的重心可不可 ...
- POJ 树的直径和重心
树的直径:(无根)树上最长两点间的最长路径,两次dfs即可,第一次dfs任选一点u,找到距离它最远的点s,再从点s进行一次dfs,找到距离s最远的点t,则s-t之间的路径就是树的直径.证明: < ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市
P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...
- [TJOI2017]城市 【树的直径+暴力+优化】
Online Judge:Luogu P3761 Label:树的直径,暴力 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有n座城市,n-1条高速公路,保证了 ...
- poj2631 求树的直径裸题
题目链接:http://poj.org/problem?id=2631 题意:给出一棵树的两边结点以及权重,就这条路上的最长路. 思路:求实求树的直径. 这里给出树的直径的证明: 主要是利用了反证法: ...
随机推荐
- htaccess在线生成工具用法大全 (转)
对于一个不懂程序的SEOER来做,更改代码方面是一件非常苦难的事情,当我们遇到301转向以及404页面的制作问题时,经常会困恼我们,这里我提供一个htaccess在线生成工具,这里有404页面链接生成 ...
- std::thread线程库详解(2)
目录 目录 简介 最基本的锁 std::mutex 使用 方法和属性 递归锁 std::recursive_mutex 共享锁 std::shared_mutex (C++17) 带超时的锁 总结 简 ...
- OpenOCD安装与使用(JTAG调试)
本文介绍openocd开源软件的安装以及搭配JTAG对Xilinx u500VC707devkit的调试 PC OS: Ubuntu20.04 LTS Target ARCH: riscv64 JTA ...
- Go语言从入门到放弃(四)
前言 有段时间没摸Go语言了,最近B站的Go语言泄露挺火的. 还差的很远呐 学无止境 本章主要介绍一些零碎的小知识点 变更记录 # 19.4.30 起笔 # 19.4.30 增加代码打包步骤 正文 ...
- 大数相加Java
题目 以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回. 分析 两个字符串,定义两个指针,分别从这两个字符串的结尾开始遍历,因为可能字符串1比字符串2长度要长,因此只要两者其中有 ...
- LeetCode-151-中等-翻转字符串里面的单词
问题描述 给定一个字符串,逐个翻转字符串中的每个单词. 说明: 无空格字符构成一个 单词 . 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括. 如果两个单词间有多余的空格,将反转 ...
- git 遇到 fatal: loose object xxxx (stored in .git/objects/cb/xxxx) is corrupt 问题
我的git版本是2.3.x,用下面这个参考链接的方法也可以解决 参考blog
- /var/lib/zabbix/percona/scripts/get_mysql_stats_wrapper.sh: line 19: mysql: command not found
[root@test ~]# tail -f /tmp/zabbix_agentd.log /var/lib/zabbix/percona/scripts/get_mysql_stats_wrappe ...
- 【Linux】Centos7 安装redis最新稳定版及问题解决
------------------------------------------------------------------------------------------------- | ...
- 【Oracle】delete表后commit后怎么找回,方法
有些时候,不小心删除了一些需要的表,而且数据库不能停止,只能一直运行下去,这样的话很麻烦 下面介绍的方法就是删除表后通过时间戳后者scn找回删除的数据 模拟实验环境: 创建一个新表 SQL> c ...