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. 数据库MySQL--数据操作语言DML(插入、修改、删除)

    例子文件:https://files-cdn.cnblogs.com/files/Vera-y/girls.zip 一.插入 方式一: 语法: insert into 表名(列名,.....) val ...

  2. [JZOJ 5778] 没有硝烟的战争

    思路: 记\(dp[i][j] = 0/1\)来表示第\(i\)个动物报的数字是\(j\),有无必胜策略. 判断有没有转移就可以了. 输出直接对于每一只动物,看\(dp[i][1->k]\)有没 ...

  3. windows 映射samba Linux服务器,输入正确的账号密码却提示“ 指定的网络密码不正确

    重启Linux samba服务也没用,重启Linux和windows系统也没用,急!!! 最佳答案 linux中要添加对应的系统用户和samba用户useradd titiansmbpasswd -a ...

  4. Hive学习详细版

    一.概述 1.Hadoop的开发问题 只能用java语言开发,存在语言门槛 需要对Hadoop底层原理,api比较了解才能做开发 开发调试比较麻烦 2.什么是Hive Hive是基于Hadoop的一个 ...

  5. 18-6-calsslist

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 窥探C语言程序的编译、链接与.h文件

    概述 C语言程序从源文件经过编译.链接生成可执行文件.那么编译与链接分别做了什么? 开发中为什么使用.h编写函数的声明?接下来使用案例说清楚为什么这样编写代码. C语言程序的编译和链接 C语言程序从源 ...

  7. Java开发系列-文件上传

    概述 Java开发中文件上传的方式有很多,常见的有servlet3.0.common-fileUpload.框架.不管哪种方式,对于文件上传的本质是不变的. 文件上传的准备 文件上传需要客户端跟服务都 ...

  8. Idea 2018.2.5创建springboot项目依赖包没有的错误

  9. Bootstrap Paginator分页插件(mark)

    Bootstrap Paginator分页插件

  10. [转]C#操作Excel初探

    近期一段时间正好在做winform导出Excel报表的问题,学习了一下C#操作Excel的一些方法(如:向Excel中插入图片:删除Excel指定sheet中的某行或某列,在Excel指定的单元格中画 ...