AcWing 1078. 旅游规划 (DFS找树的直径+直径中点性质求解,无DP)

news/2024/11/15 21:56:52/文章来源:https://www.cnblogs.com/jerrycyx/p/18372359

原题链接

题目描述

image


算法

引用自 树的直径 - OI-Wiki:

若树上所有边边权均为正,则树的所有直径中点重合
证明:使用反证法。设两条中点不重合的直径分别为 \(\delta(s,t) 与 \delta(s',t')\),中点分别为 \(x\)\(x'\)。显然,\(\delta(s,x) = \delta(x,t) = \delta(s',x') = \delta(x',t')\)

无负权边的树所有直径的中点重合

\(\delta(s,t') = \delta(s,x) + \delta(x,x') + \delta(x',t') > \delta(s,x) + \delta(x,t) = \delta(s,t)\),与 \(\delta(s,t)\) 为树上任意两节点之间最长的简单路径矛盾,故性质得证。

补充说明:当树的直径长度为偶数的时候,所有直径必定经过两中点及其间的边,证明与上文类似。

根据以上内容,所有直径必过中点且一定以其为自己的中点,所以总体思路为找到中点后,从中点出发找到与中点距离为直径一半的点,即为直径一端点

所以,我们可以两次 DFS 找出树直径的两端点,然后再一次 DFS 找到树的直径的一个或两个中点(已知直径两端点之间路径上距离一端点 直径>>1(直径+1)>>1 的点,可能有两个中点),最后从一个或两个中点分别 DFS 找与中点距离为直径一半的点,找到后回溯时把路径上所有点都打上标记,最后输出有标记的点即可。

注意一个细节:当有两个中点时,为了避免多找或漏找,可以在将判断条件设置为 距离>=(直径+1)>>1,这样当直径为偶数时(此时中点只有一个)+1 不影响判断;当直径为奇数(此时有两个中点),向上取整可以使每一个中点都找到更靠近另一个中点的端点。否则如果不向上取整的话,可能找到在另一个中点那边距离自己 直径>>1 的点,但实际上直径应当是自己这边的一端距离自己 直径>>1,另一个中点的那一端距离自己 (直径+1)>>1(即距离另一个中点 直径>>1),所以会把不是端点的点判为端点,就 WA 了(被这个坑了好几次)。

时间复杂度

五次 DFS 均为 \(O(N)\),总时间复杂度为 \(O(N)\)

C++ 代码

#include<cstdio>
#include<cstring>
using namespace std;const int N=2e5+5;
int n;struct Allan{int to,nxt;
}edge[N<<1];
int head[N],idx;
inline void add(int x,int y)
{edge[++idx]={y,head[x]};head[x]=idx;return;
}void DFS_in(int *len,int x,int fa=0)
{for(int i=head[x];i;i=edge[i].nxt){int y=edge[i].to;if(y==fa) continue;len[y]=len[x]+1;DFS_in(len,y,x);}return;
}int pos1,pos2,diam,core1,core2;
int len_core[N];
bool is_diam[N];
bool DFS_find_path(int x,int fa=0)
{if(len_core[x]>=(diam+1)>>1)return is_diam[x]=true;bool on_diam=false;for(int i=head[x];i;i=edge[i].nxt){int y=edge[i].to;if(y==fa) continue;len_core[y]=len_core[x]+1;on_diam|=DFS_find_path(y,x);}is_diam[x]|=on_diam;return on_diam;
}int len1[N],len2[N];
bool DFS_find_core(int x,int fa=0)
{if(x==pos2) return true;bool on_diam=false;for(int i=head[x];i;i=edge[i].nxt){int y=edge[i].to;if(y==fa) continue;on_diam|=DFS_find_core(y,x);if(on_diam&&len2[x]==diam>>1) core1=x;if(on_diam&&len2[x]==(diam+1)>>1) core2=x;}return on_diam;
}int main()
{scanf("%d",&n);for(int i=1;i<n;i++){int x,y; scanf("%d%d",&x,&y);x++,y++;add(x,y),add(y,x);}DFS_in(len1,1);for(int i=1;i<=n;i++)if(len1[i]>len1[pos1]) pos1=i;DFS_in(len2,pos1);for(int i=1;i<=n;i++)if(len2[i]>len2[pos2]) pos2=i;diam=len2[pos2];DFS_find_core(pos1);DFS_find_path(core1);memset(len_core,0,sizeof(len_core));DFS_find_path(core2);for(int i=1;i<=n;i++)if(is_diam[i]) printf("%d\n",i-1);return 0;
}

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

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

相关文章

世上最好的共享内存(Linux共享内存最透彻的一篇)------宋宝华

共享单车、共享充电宝、共享雨伞,世间的共享有千万种,而我独爱共享内存。 早期的共享内存,着重于强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),以便于CPU可以在各个进程访问到这片内存。现阶段广泛应用于多媒体、Graphics领域的共享内存方…

P7706 文文的摄影布置 题解

一道不错的线段树题P7706 文文的摄影布置 题解 原题 读完题,发现是线段树。单点修改+区间查询。 不过查询的值有些奇怪,就是了,我们考虑用线段树维护这个 ψ 值(下称待求值)。 对于一个区间的待求值,大概有四种情况:如上图四种情况分别为:待求值最大值在左区间 待求值最大…

信息学奥赛初赛天天练-71-NOIP2016普及组-基础题2-进制转换、二进制转八进制、八进制转二进制、二叉树数组存储、寻址空间

NOIP 2016 普及组 基础题2 4 以下不是 CPU 生产厂商的是( ) A Intel B AMD C Microsoft D IBM 8 与二进制小数 0.1相等的八进制数是( ) A 0.8 B 0.4 C 0.2 D 0.1 9 以下是 32 位机器和 64 位机器的区别是( ) A 显示器不同 B 硬盘大小不同 C 寻…

CF 2001 E2 solution (967 Div.2)

CF 2001 E2 由于对称,所以设 \(heap[u]\) 为两次确定堆,且第一次弹出的是 \(u\),\(heap[u,v]\) 是第一次 \(u\) ,第二次 \(v\) 则答案就是 \(\sum heap[u]=2^{n-1}heap[x]\) 其中 \(x\) 任意。 不妨我们考虑第一次都是从第一个叶子弹出,那么对于其他不同的第二个弹出的点…

pytest和unittest的几个区别

主要区别1、安装需求:  pytest:作为第三方单元测试库,需要额外安装。  unittest:是Python标准库的一部分,无需额外安装。2、用例编写规则:  pytest:编写规则较为简单,兼容性好,可以执行unittest风格的测试用例,无需修改unittest用例的任何代码。  unittest:…

【Linux】分区向左扩容的方法

@目录为什么是向左扩容操作前的备份方法:启动盘试用Ubuntu后进行操作为什么是向左扩容 Linux向右扩容非常简单,无论是系统自带的disks工具还是apt安装的gparted工具,都有图像化的界面可以操作。但是,都不支持向左扩容。笔者这里的磁盘情况如下:其中磁盘1的99.5GB是我的Lin…

linux开机自启脚本运行完之后不关闭应用程序

最近在用petalinux2323.2版本编译的系统镜像,想在系统开机后自动运行程序,发现按照2021.2相同的方式启动之后,软件在开机脚本运行完就会自动退出了,经过查找发现petalinux2023.2编译的镜像默认使用的systemd去运行开机脚本,而2021.2是使用的init.d。在systemd方式下需要在…

读懂财务报表:解密资产负债率与净资产收益率

一、概述 财务报表中隐藏着大量的信息,如果我们在解读时缺乏系统的思路或忽略了关键指标,就很容易被庞杂的数据搞得不知所措。本文将从几个重要指标出发,包括资产负债率、净资产收益率和销售复合增长率的计算与分析,帮助大家更精准地理解财务报表。二、财务报表的重点 首先…

陶瓷基板

【附在每个笔记前面:个人学习记录,如有错误,烦请指正,不胜感激。】一、组成:基片+膜层(基于行业局限性,暂时只看金属化薄膜基板,厚膜下次找机会再学习下) ① 基片材质: 氧化铝、氮化铝、碳化硅、金刚石 受制于成形工艺:即烧型、研磨型、抛光型在公差、表面粗糙…

人工智能 | 结对编程助手GithubCopilot

简介 GitHub Copilot 是一款 AI 结对程序员,可帮助您更快、更少地编写代码。它从注释和代码中提取上下文,以立即建议单独的行和整个函数。GitHub Copilot 由 GitHub、OpenAI 和 Microsoft 开发的生成式 AI 模型提供支持。它可作为 Visual Studio Code、Visual Studio、Neovim…

iLogtail 开源两周年:感恩遇见,畅想未来

iLogtail 的诞生初衷非常朴素,那就是开发一款轻量、高性能、高可靠的可观测数据采集器。也是基于这样一个常见且迫切的需求,iLogtail 于 2013 年在阿里巴巴诞生。早在上世纪 60 年代,早期的计算机(例如 ENIAC 和 IBM 的大型机)在操作过程中会输出一些基本的状态信息和错误…

ant design vue 表格table 和复选框Checkbox结合 实现树形数据操作

前言:最近在做一个权限管理的页面,需要配置权限。业务给的要求在表格里,展示权限以及编辑权限。然后根据权限数组,动态展示模块。 页面需求:可以设定每个公司或者部门的单独权限,可以编辑保存权限 主要实现: 1.全选,反选(递归循环,every,some实现) 2.子级选中其父级…