Online JudgeLuogu 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]城市 【树的直径+暴力+优化】的更多相关文章

  1. [TJOI2017] 城市 (树的直径,贪心)

    题目链接 Solution 这道题,调了我一晚上... 一直80分 >_<|| ... 考虑到几点: 分开任意一条边 \(u\) ,那么其肯定会断成两棵树. 肯定是分开直径上的边最优,否则 ...

  2. luogu P3761 [TJOI2017]城市 树的直径 bfs

    LINK:城市 谢邀,学弟说的一道毒瘤题. 没有真正的省选题目毒瘤 或者说 写O(n)的做法确实毒瘤. 这里给一个花20min就写完的非常好写的暴力. 容易想到枚举哪条边删掉 删掉之后考虑在哪两个点上 ...

  3. bzoj4890[Tjoi2017]城市(树的半径)

    4890: [Tjoi2017]城市 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 149  Solved: 91[Submit][Status][D ...

  4. [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分

    题目链接: [JOI 2019 Final]独特的城市 对于每个点,它的答案最大就是与它距离最远的点的距离. 而如果与它距离为$x$的点有大于等于两个,那么与它距离小于等于$x$的点都不会被计入答案. ...

  5. LG5536 「XR-3」核心城市 树的直径

    问题描述 LG5536 题解 两次 \(\mathrm{dfs}\) 求树的直径. 然后找到树的直径的中点. 然后按照 子树中最深的点深度-自己深度 排序,贪心选取前 \(k\) 个. \(\math ...

  6. [NOI2003]逃学的小孩 (贪心+树的直径+暴力枚举)

    Input 第一行是两个整数N(3 <= N <= 200000)和M,分别表示居住点总数和街道总数.以下M行,每行给出一条街道的信息.第i+1行包含整数Ui.Vi.Ti(1<=Ui ...

  7. luogu题解 P1099 【树网的核】树的直径变式+数据结构维护

    题目链接: https://www.luogu.org/problemnew/show/P1099 https://www.lydsy.com/JudgeOnline/problem.php?id=1 ...

  8. [TJOI2017]城市(树的直径)

    [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达, ...

  9. 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市

    P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...

随机推荐

  1. 【JZOJ6354】最短路(tiring)

    description analysis 显然边权有变化规律\(x,{1\over{x-1}},{x-1\over x},x,...\) 于是把一个点拆成三个点,分别表示步数到除\(3\)余\(0,1 ...

  2. SQL Server SQLFetch()

    { /* 摘要 SQLFetch从结果集中提取下一个数据行集, 并返回所有绑定列的数据. 语法 C++ SQLRETURN SQLFetch( SQLHSTMT     StatementHandle ...

  3. 专访阿里云MVP黄胜蓝:90 后 CTO花了6年,改变了你日常生活里的这件事

    [黄胜蓝:现任武汉极意网络科技有限公司CTO.高中时期NOIP一等奖保送至武汉大学,大学期间曾指导团队获得世界数学建模大赛金奖,同时负责武汉大学学生校园门户网站的运维工作.于2013年加入武汉极意网络 ...

  4. 计算几何——直线交点poj1269

    求直线交点还是要推一个公式的.. 见博客https://blog.csdn.net/u013050857/article/details/40923789 还要学一下向量的定点比分法 另外poj精度好 ...

  5. Linux后台运行java的jar包后台运行java -jar 命令

    为什么java -jar 的命令终端的窗口关闭就停止运行了??tomcat中war的就不会? 关闭终端的窗口相当于ctrl+c的命令,关闭了窗口就相当于停止了java -jar这个进程,即ctrl+c ...

  6. Spring MVC(十六)--Spring MVC国际化实例

    上一篇文章总结了一下Spring MVC中实现国际化所需的配置,本文继上一文举一个完整的例子,我选择用XML的方式.我的场景是这样的: 访问一个页面时,这个页面有个表格,对表头中的列名实现国际化. 第 ...

  7. java web项目部署到云服务器

    第一步把java web项目打包 成war包 第二步:在Build选里选择build Artfacts->water:war->Build war包建立完毕. 第三步:在官网下载winsc ...

  8. map 与 lambda 的用法

    # 列表中的每个元素进行*2lis = [10, 30, 70]f = map(lambda li: li*2, lis) # 操作lis中的每个元素.print(list(f)) # 返回来一个新的 ...

  9. shell 一些题目

    在a.log中精确查找含有msyql单词的行a.log文件内容如下: mysqlmysql mysqlmysql aa mysql_mysqla mysql b_mysql aa _mysqla _m ...

  10. 获取调用U9接口时报错的方法