[TJOI2017]城市 【树的直径+暴力+优化】
Online Judge:Luogu P3761
Label:树的直径,暴力
题目描述
从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作。这个地区一共有n座城市,n-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达,但是通过一条高速公路需要收取一定的交通费用。小明对这个地区深入研究后,觉得这个地区的交通费用太贵。
小明想彻底改造这个地区,但是由于上司给他的资源有限,因而小明现在只能对一条高速公路进行改造,改造的方式就是去掉一条高速公路,并且重新修建一条一样的高速公路(即交通费用一样),使得这个地区的两个城市之间的最大交通费用最小(即使得交通费用最大的两座城市之间的交通费用最小),并且保证修建完之后任意两座城市相互可达。如果你是小明,你怎么解决这个问题?
输入
输入数据的第一行为一个整数n,代表城市个数。
接下来的n-1行分别代表了最初的n-1条公路情况。每一行都有三个整数u,v,d。u,v代表这条公路的两端城市标号,d代表这条公路的交通费用。
\(1 <= u,v <= n\),\(1<= d <= 2000\)
输出
输出数据仅有一行,一个整数,表示进行了最优的改造之后,该地区两城市 之间最大交通费用。
样例
Input
5
1 2 1
2 3 2
3 4 3
4 5 4
Output
7
说明/提示
对于30%的数据,1<=n<500
对于100%的数据,1<=n<=5000
题解
首先说一下这道题最优时间复杂度是\(O(N)\)的。
但对于\(n<=5000\)的数据,\(O(N^2)\)加上一些优化也跑的飞快。
一、最基础的做法
直接暴力\(O(N)\)枚举每条边,思考断掉这条边,如何重连使得最大交通费用最小,并且如何去求。
断掉一条边后,在断口处会形成两棵子树。在还没连边之前,这两棵子树的直径都有可能成为答案。接下来如何重连边呢,很明显是将两棵子树的重心相连,随之产生的另一个备选答案就是两棵子树的半径之和。
综上答案为\(ans=max(直径1,max(直径2,半径1+半径2))\)。
A子树的直径求法:
法1:dfs两遍,同时找到两个端点(下面代码采用)
法2:dfs一遍,维护最长/次长链
B子树的重心、半径求法
需要注意的是,只有确定了重心才能确定半径。
所谓重心是指树的直径上的中点,而半径是到两端点距离较大的一段距离。
所以可以分别以直径上的两个端点为源点跑一遍dfs,处理出每个点到这两点的距离,然后先求重心、再求半径。
求子树的直径是O(N)的,求子树的重心、半径也是O(N)的。所以整个算法的时间复杂度为\(O(N^2)\)。
二、优化
1.枚举所有边->枚举直径上的边。
2.既然是枚举直径上的边,稍稍观察一下可以知道,两棵子树的某个直径端点就是原直径的一个端点,这样后面就只用dfs一遍即可求出直径了,并且每个点离端点的距离也可以在此时求出,这样后面就不用再多dfs一遍处理每个点离直径端点的距离了,而找重心也只用在直径上找。
上面的优化看似都是常数上的优化,但程序效率却大大提升。。

当然如果继续优化可以做到O(N)。
O(N)
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=5010,INF=12345678;
struct edge{
int to,d,nxt;
}e[2*N];
int head[N],Ecnt,n;
inline int link(int u,int v,int d){
e[++Ecnt].to=v,e[Ecnt].d=d,e[Ecnt].nxt=head[u];
head[u]=Ecnt;
}
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
int id1,id2,ma;
int pa[2],pb[2],dia[2];
int nowx,nowl;
int d[2][N],li[2][N];
void dfs(int x,int fa,int dis,int no,int g){
if(dis>nowl)nowl=dis,nowx=x;
d[g][x]=dis;
li[g][x]=fa;
for(register int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(y==fa||y==no)continue;
dfs(y,x,dis+e[i].d,no,g);
}
}
inline void go(int root,int ban,int g){
nowx=nowl=0;
dfs(pa[g],0,0,ban,g);
pb[g]=nowx,dia[g]=nowl;
}
inline int midpoint(int g,int ban){
int mid=0,mi=INF;
for(register int i=pb[g];i;i=li[g][i]){
int data=abs(dia[g]-2*d[g][i]);
if(data<mi)mi=data,mid=i;
}
return max(d[g][mid],dia[g]-d[g][mid]);
}
inline int calc(int u,int v,int d){
go(u,v,0);go(v,u,1);
int linklen=midpoint(0,v)+midpoint(1,u)+d;
return max(linklen,max(dia[0],dia[1]));
}
int nxt[N],nxtlen[N];
void alltree(int x,int fa,int dis){
if(dis>nowl)nowl=dis,nowx=x;
nxt[x]=fa;
for(register int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(y==fa)continue;
nxtlen[y]=e[i].d;
alltree(y,x,dis+e[i].d);
}
}
int main(){
scanf("%d",&n);
register int i;
for(i=1;i<n;i++){
int u=read(),v=read(),d=read();
link(u,v,d),link(v,u,d);
}
int old1,old2;
alltree(1,0,0);
old1=nowx;nowx=nowl=0;
alltree(old1,0,0);
old2=nowx;nowx=nowl=0;
pa[1]=old1,pa[0]=old2;
int ans=INF;
for(i=old2;i;i=nxt[i]){
if(nxt[i]==0)continue;
ans=min(ans,calc(i,nxt[i],nxtlen[i]));
}
printf("%d\n",ans);
}
[TJOI2017]城市 【树的直径+暴力+优化】的更多相关文章
- [TJOI2017] 城市 (树的直径,贪心)
题目链接 Solution 这道题,调了我一晚上... 一直80分 >_<|| ... 考虑到几点: 分开任意一条边 \(u\) ,那么其肯定会断成两棵树. 肯定是分开直径上的边最优,否则 ...
- luogu P3761 [TJOI2017]城市 树的直径 bfs
LINK:城市 谢邀,学弟说的一道毒瘤题. 没有真正的省选题目毒瘤 或者说 写O(n)的做法确实毒瘤. 这里给一个花20min就写完的非常好写的暴力. 容易想到枚举哪条边删掉 删掉之后考虑在哪两个点上 ...
- bzoj4890[Tjoi2017]城市(树的半径)
4890: [Tjoi2017]城市 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 149 Solved: 91[Submit][Status][D ...
- [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分
题目链接: [JOI 2019 Final]独特的城市 对于每个点,它的答案最大就是与它距离最远的点的距离. 而如果与它距离为$x$的点有大于等于两个,那么与它距离小于等于$x$的点都不会被计入答案. ...
- LG5536 「XR-3」核心城市 树的直径
问题描述 LG5536 题解 两次 \(\mathrm{dfs}\) 求树的直径. 然后找到树的直径的中点. 然后按照 子树中最深的点深度-自己深度 排序,贪心选取前 \(k\) 个. \(\math ...
- [NOI2003]逃学的小孩 (贪心+树的直径+暴力枚举)
Input 第一行是两个整数N(3 <= N <= 200000)和M,分别表示居住点总数和街道总数.以下M行,每行给出一条街道的信息.第i+1行包含整数Ui.Vi.Ti(1<=Ui ...
- luogu题解 P1099 【树网的核】树的直径变式+数据结构维护
题目链接: https://www.luogu.org/problemnew/show/P1099 https://www.lydsy.com/JudgeOnline/problem.php?id=1 ...
- [TJOI2017]城市(树的直径)
[TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达, ...
- 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市
P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...
随机推荐
- vsftp 被动模式配置
直接复制粘切过来就能用 这里只讲下配置,安装方法可以直接yum 配置文件修改 anonymous_enable=NO #关闭匿名用户 xferlog_file=/var/log/vsftpd.log ...
- linux watch命令查看网卡流量
watch命令可以反复的执行一个命令,默认时间间隔为2秒钟.TX是发送(transport),RX是接收(receive)RX bytes:总下行流量TX bytes:总上行流量 可以每隔两秒监视网络 ...
- 训练计划Day1
Day1:二分答案,三分查找,快速幂,欧拉筛素数 | 题目:火星人,Bridge,GCD,Prime Path 二分答案 [JSOI 2008] 火星人 对于第一个操作用\(hash + 二分\)来求 ...
- How to Hide Apache Tomcat Version Number from Error Pages
1. 进入tomcat lib目录 cd /usr/local/tomcat7/lib 2. 解压catalina.jar jar xvf catalina.jar 3. 修改ServerInfo.p ...
- 控制变量行业年份回归时在STATA里怎么操作_stata 分年份回归
控制变量行业年份回归时在STATA里怎么操作_stata 分年份回归 我希望做一个多元回归,但需要控制年份和行业. (1)年份有7年2006-2012,听说STATA可以自动设置虚拟变量,请问命令是怎 ...
- STM32笔记——Power Controller(PWR)
The device requires a 1.8 to 3.6 V operating voltage supply (VDD). An embedded linear voltage regula ...
- scrapy - 给scrapy 的spider 传值
scrapy - 给scrapy 的spider 传值 方法一: 在命令行用crawl控制spider爬取的时候,加上-a选项,例如: scrapy crawl myspider -a categor ...
- [AHOI2014/JSOI2014]骑士游戏
题目 思博贪心题写了一个半小时没救了,我也没看出这是一个\(spfa\)来啊 设\(dp_i\)表示彻底干掉第\(i\)只怪物的最小花费,一个非常显然的事情,就是对于\(k_i\)值最小的怪物满足\( ...
- while语句结构(for循环)
while语句结构(for循环) python for 循环可以遍历任何序列的项目,如一个列表或一个字符串 for循环的一般形式 for 条件判断 in 一个序列: 执行语句 else: 执行语句 可 ...
- (转)Linux使用RSA密钥登录远程服务器
一切操作都在本机执行,不需要进入远程主机/服务器~~ 1.生成密钥.默认生成的是rsa加密. ssh-keygen 2.私钥是给本地的,公钥是给远程的.下面将公钥上传到远程服务器 ~ ssh-copy ...