P3761 [TJOI2017]城市

题目描述

从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作。这个地区一共有ri座城市,《-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达,但是通过一条高速公路需要收取一定的交通费用。小明对这个地区深入研究后,觉得这个地区的交通费用太贵。小明想彻底改造这个地区,但是由于上司给他的资源有限,因而小明现在只能对一条高速公路进行改造,改造的方式就是去掉一条高速公路,并且重新修建一条一样的高速公路(即交通费用一样),使得这个地区的两个城市之间的最大交通费用最小(即使得交通费用最大的两座城市之间的交通费用最小),并且保证修建完之后任意两座城市相互可达。如果你是小明,你怎么解决这个问题?

输入输出格式

输入格式:

输入数据的第一行为一个整数n,代表城市个数。

接下来的n - 1行分别代表了最初的n-1条公路情况。每一行都有三个整数u,v,d。u,v代表这条公路的两端城市标号,d代表这条公路的交通费用。

1 <= u,v <= n,1<= d <= 2000

输出格式:

输出数据仅有一行,一个整数,表示进行了最优的改造之后,该地区两城市 之间最大交通费用。

这道题做了一晚上。

好题。

一句话题意:给你一棵树,让你可以去掉一条边,再在别的位置上加上一条边权相等的边,使得这个图还是一棵树,并且在这棵树的前提下,相距最远的两个点的距离最小。

首先很容易知道我们要换的边一定在原树的直径上,反证法。

所以求出树的直径打上标记,然后枚举拆去哪一条边就行了。

我们拆去一条边之后,原来的树会变成两棵新树,我们可以将这两棵新树任意合并,要求的是合并之后的新树的直径。

可以分成三种情况:首先有可能是两颗小树的直径之一。

​ 第三种情况是当我们把这两棵树的重心连在一起之后,新树的直径:也就是原来两棵子树中以重心为根的前提下的最长链之和加上我们当前拆的这条边的边权。

前两种比较好求,直接还是按照求直径的方法来就行,注意区分两棵子树。

第三种的话,我们需要求出当前树的重心和到重心的最长链。

怎么求?这里用到了换根DP,这也是合并树求直径的常用套路。

首先我们设\(f[u]\)表示以u为根的子树中,到u的最长链,那么枚举子节点,先将当前枚举到的子节点DP出来,然后转移即可。转移方程:\(f(u)=max(f(u),f(v)+edge(i).dis)\)

同时,还需要维护一个次长链,后面有用,跟着f转移就行。

类似点分治求重心的思想,我们可以发现对于u,还有他上面的一条链没有算,这个时候就要进行换根,所以我们的第二遍DP是进行换根,求出以u为根的全局最长链,XJB转移一下就可以了。

code:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. //#define int long long
  5. using namespace std;
  6. const int wx=5017;
  7. inline int read(){
  8. int sum=0,f=1; char ch=getchar();
  9. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  10. while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();}
  11. return sum*f;
  12. }
  13. int totx,toty,num,n,m,pos,tmp,root,maxx,maxy,ans=2147483642;
  14. int head[wx],dis[wx],vis[wx],visx[wx],visy[wx],flag[wx];
  15. int f[wx],size[wx],g[wx],ff[wx],gg[wx];
  16. struct e{
  17. int nxt,to,dis;
  18. }edge[wx*2];
  19. void add(int from,int to,int dis){
  20. edge[++num].nxt=head[from];
  21. edge[num].to=to;
  22. edge[num].dis=dis;
  23. head[from]=num;
  24. }
  25. void dfs_1(int u,int fa){
  26. for(int i=head[u];i;i=edge[i].nxt){
  27. int v=edge[i].to;
  28. if(v==fa)continue;
  29. dis[v]=dis[u]+edge[i].dis;
  30. dfs_1(v,u);
  31. }
  32. }
  33. void dfs_2(int u,int fa){
  34. for(int i=head[u];i;i=edge[i].nxt){
  35. int v=edge[i].to;
  36. if(v==fa)continue;
  37. dis[v]=dis[u]+edge[i].dis;
  38. dfs_2(v,u);
  39. }
  40. }
  41. bool dfs_3(int u,int fa){
  42. for(int i=head[u];i;i=edge[i].nxt){
  43. int v=edge[i].to;
  44. if(v==fa)continue;
  45. if(dfs_3(v,u)||v==pos){
  46. flag[i]=1;
  47. return true;
  48. }
  49. }
  50. return false;
  51. }
  52. void init(){
  53. n=read();
  54. for(int i=1;i<n;i++){
  55. int x,y,z;
  56. x=read(); y=read(); z=read();
  57. add(x,y,z); add(y,x,z);
  58. }
  59. }
  60. void find_length(){
  61. dfs_1(1,0);
  62. for(int i=1;i<=n;i++){
  63. if(dis[i]>tmp){
  64. root=i;
  65. tmp=dis[i];
  66. }
  67. }
  68. memset(dis,0,sizeof dis);
  69. dfs_2(root,0);
  70. tmp=0;
  71. for(int i=1;i<=n;i++){
  72. if(dis[i]>tmp){
  73. pos=i;
  74. tmp=dis[i];
  75. }
  76. }
  77. dfs_3(root,0);//起点root,终点pos
  78. }
  79. void dfs_5(int u,int fa){
  80. visx[u]=1;
  81. for(int i=head[u];i;i=edge[i].nxt){
  82. int v=edge[i].to;
  83. if(v==fa||visy[u])continue;
  84. dis[v]=dis[u]+edge[i].dis;
  85. totx+=edge[i].dis;
  86. dfs_5(v,u);
  87. }
  88. }
  89. void dfs_6(int u,int fa){
  90. visy[u]=1;
  91. for(int i=head[u];i;i=edge[i].nxt){
  92. int v=edge[i].to;
  93. if(v==fa||visx[u])continue;
  94. dis[v]=dis[u]+edge[i].dis;
  95. toty+=edge[i].dis;
  96. dfs_6(v,u);
  97. }
  98. }
  99. void dfs_7(int u){
  100. vis[u]=1;
  101. for(int i=head[u];i;i=edge[i].nxt){
  102. int v=edge[i].to;
  103. if(!visx[v]||vis[v])continue;
  104. dis[v]=dis[u]+edge[i].dis;
  105. dfs_7(v);
  106. }
  107. }
  108. void dfs_8(int u){
  109. vis[u]=1;
  110. for(int i=head[u];i;i=edge[i].nxt){
  111. int v=edge[i].to;
  112. if(!visy[v]||vis[v])continue;
  113. dis[v]=dis[u]+edge[i].dis;
  114. dfs_8(v);
  115. }
  116. }
  117. void dp_1(int u,int fa){
  118. vis[u]=1;
  119. f[u]=g[u]=0;
  120. for(int i=head[u];i;i=edge[i].nxt){
  121. int v=edge[i].to;
  122. if(vis[v]||v==fa)continue;
  123. dp_1(v,u);
  124. if(f[u]<f[v]+edge[i].dis){
  125. g[u]=f[u];
  126. f[u]=f[v]+edge[i].dis;
  127. }
  128. else if(g[u]<f[v]+edge[i].dis){
  129. g[u]=f[v]+edge[i].dis;
  130. }
  131. }
  132. vis[u]=0;
  133. }
  134. int dfs2(int u,int fa){
  135. vis[u]=1;
  136. int re=f[u],w;
  137. for(int i=head[u];i;i=edge[i].nxt){
  138. int v=edge[i].to;
  139. if(vis[v]||v==fa)continue;
  140. if(f[u]==f[v]+edge[i].dis){
  141. w=edge[i].dis+g[u];
  142. }
  143. else w=edge[i].dis+f[u];
  144. if(w>=f[v]){
  145. g[v]=f[v];f[v]=w;
  146. }
  147. else if(w>g[v]){
  148. g[v]=w;
  149. }
  150. re=min(re,dfs2(v,u));
  151. }
  152. vis[u]=0;
  153. return re;
  154. }
  155. void find_ans(int x,int y,int now){
  156. memset(visx,0,sizeof visx);
  157. memset(visy,0,sizeof visy);
  158. memset(dis,0,sizeof dis);
  159. memset(vis,0,sizeof vis);
  160. // cout<<x<<"zz"<<y<<endl;
  161. int re=0;
  162. dp_1(x,y);re=dfs2(x,y);
  163. dp_1(y,x);re+=dfs2(y,x)+now;
  164. totx=0; toty=0;
  165. dfs_5(x,y); dfs_6(y,x);//将所有点划分到x和y的两个集合内 //接下来去找两棵子树中的重心
  166. int tmpx=0,tmpy=0;
  167. int rootx=0,rooty=0;
  168. for(int i=1;i<=n;i++){
  169. if(visx[i]){
  170. if(tmpx<dis[i]){
  171. tmpx=dis[i];
  172. rootx=i;
  173. }
  174. }
  175. if(visy[i]){
  176. if(tmpy<dis[i]){
  177. tmpy=dis[i];
  178. rooty=i;
  179. }
  180. }
  181. }
  182. memset(dis,0,sizeof dis);
  183. memset(vis,0,sizeof vis);
  184. dfs_7(rootx); dfs_8(rooty);
  185. int zmjx=0,zmjy=0;
  186. for(int i=1;i<=n;i++){
  187. if(visx[i]){
  188. zmjx=max(zmjx,dis[i]);
  189. }
  190. else zmjy=max(zmjy,dis[i]);
  191. }
  192. re=max(re,max(zmjx,zmjy));
  193. ans=min(ans,re);
  194. }
  195. void dfs_4(int u,int fa){
  196. for(int i=head[u];i;i=edge[i].nxt){
  197. int v=edge[i].to;
  198. if(v==fa||!flag[i])continue;
  199. find_ans(u,v,edge[i].dis);
  200. dfs_4(v,u);
  201. }
  202. }
  203. void work(){
  204. dfs_4(root,0);
  205. printf("%d\n",ans);
  206. }
  207. signed main(){
  208. init();
  209. find_length();
  210. work();
  211. return 0;
  212. }

换根DP+树的直径【洛谷P3761】 [TJOI2017]城市的更多相关文章

  1. [洛谷P3761] [TJOI2017]城市

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

  2. P4323-[JSOI2016]独特的树叶【换根dp,树哈希】

    正题 题目链接:https://www.luogu.com.cn/problem/P4323 题目大意 给出\(n\)个点的树和加上一个点之后的树(编号打乱). 求多出来的是哪个点(如果有多少个就输出 ...

  3. [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]

    题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...

  4. 洛谷 P4284 [SHOI2014]概率充电器 概率与期望+换根DP

    洛谷 P4284 [SHOI2014]概率充电器 概率与期望+换根DP 题目描述 著名的电子产品品牌\(SHOI\) 刚刚发布了引领世界潮流的下一代电子产品-- 概率充电器: "采用全新纳米 ...

  5. 洛谷$P3647\ [APIO2014]$连珠线 换根$dp$

    正解:换根$dp$ 解题报告: 传送门! 谁能想到$9102$年了$gql$居然还没写过换根$dp$呢,,,$/kel$ 考虑固定了从哪个点开始之后,以这个点作为根,蓝线只可能是直上直下的,形如&qu ...

  6. 模拟赛:树和森林(lct.cpp) (树形DP,换根DP好题)

    题面 题解 先解决第一个子问题吧,它才是难点 Subtask_1 我们可以先用一个简单的树形DP处理出每棵树内部的dis和,记为dp0[i], 然后再用一个换根的树形DP处理出每棵树内点 i 到树内每 ...

  7. Codeforces 891D - Sloth(换根 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 换根 dp 好题. 为啥没人做/yiw 首先 \(n\) 为奇数时答案显然为 \(0\),证明显然.接下来我们着重探讨 \(n\) 是偶数 ...

  8. Codeforces 997D - Cycles in product(换根 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 一种换根 dp 的做法. 首先碰到这类题目,我们很明显不能真的把图 \(G\) 建出来,因此我们需要观察一下图 \(G\) 有哪些性质.很 ...

  9. Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)

    题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...

随机推荐

  1. 手机的RAM和ROM

    RAM是由英文Random Access Memory的首字母构成的,意为随机存储器,即在正常工作状态下可以往存储器中随时读写数据.根据存储单元工作原理的不同,RAM又可分为静态存储器(SRAM)和动 ...

  2. 5-EasyNetQ之Publish(黄亮翻译)

    EasyNetQ支持的最简单的消息模式是发布/订阅.这个模式是一个极好的方法用来解耦消息提供者和消费者.消息发布者只要简单的对世界说,"这里有些事发生" 或者 "我现在有 ...

  3. hibernate学习笔记(4)表单操作

    User.hbm.xml的表单配置: ①主键 <id name="id" type="java.lang.Integer"> <column ...

  4. 配gzip的过滤器进行压缩解决表单加载慢问题

    一个客户的表单上字段超过五百,经浏览器的调试器发现主要问题是从服务器取数据花费了大量时间,下载内容大小约1.2M,下载时间在10s左右,导致样式加载完大约在17s左右(不清除浏览器缓存).最终考虑利用 ...

  5. day35-hibernate映射 04-Hibernate的一级缓存:一级缓存的存在

    数据源:文件和数据库.从内存中获取,不用去数据库发送SQL语句查询了.缓存技术是hibernate的一个优化的手段.Session结束了,一级缓存就没了,就销毁了.SeesionFactory没了,二 ...

  6. 洛谷P3328(bzoj 4085)毒瘤线段树

    题面及大致思路:https://www.cnblogs.com/Yangrui-Blog/p/9623294.html, https://www.cnblogs.com/New-Godess/p/45 ...

  7. 利用脚本,一键设置java环境变量(默认安装路径)

    Windows一键设置Java环境变量 右击以管理员方式运行,注意自行更改JAVA_HOME目录文件安装目录. JDKSetting.bat @echo off color 0a echo.----- ...

  8. 面试题:Concurrenthashmap原理分析 有用

    一.背景: 线程不安全的HashMap     因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap.   效率低下的H ...

  9. HDOJ 1023 Train Problem II 卡特兰数

    火车进站出站的问题满足卡特兰数...卡特兰数的相关知识如下: 卡特兰数又称卡塔兰数,是组合数学中一个常出现在各种计数问题中出现的数列.由以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)命名. ...

  10. 每日一Git计划启动

    效仿某大神,每日一Git,初次启动,维时30天 规范: 1. 不能提交纯属搞笑灌水的 2. 可以提交ACM题目代码 3. 想不到了..靠自觉的东西,对自己有提升的就做,没提升纯属刷数据的就不算吧.. ...