题目:

  https://www.lydsy.com/JudgeOnline/problem.php?id=1912

题解:

  首先,显然当不加边的时候,遍历一棵树每条边都要经过两次。那么现在考虑k==1的情况,考虑加入的这一条边有什么作用。

显然,如图4边的作用就是使得原来的1-2-3-3-2-1路线变为了4-3-2-1或1-2-3-4,那么作用就是以多走一步的代价使得这条新边两端的两个结点的遍历路径长度减半。

  因此,想要使路径最短,就要使这条新边两端的两个结点之间的距离更长,显然,当两端的结点在原树中的路径为原树的直径时取得k==1时的最优解。设直径长度为dis,则k==1时ans=(n-1)*2-dis+1。

  那么,k==2的时候怎么办呢。显然直接求次长链的做法是错的,因为求次长链的时候会与直径的某些边重复,那么这些边就是没有意义的,答案显然不更优。

  因此,我们采用另一种方法:将直径上的边权全部置成-1,然后再跑一遍最长链。开始我也很不懂为什么要置成-1而不置成0,欸还是上面那张图是因为你考虑当你通过4边走到最下面那个点之后,显然你还是要回到最初的点的,而如果路径上的边与之前的直径重复,那么等于说你多加了一遍这条边,因此置成-1后不就相当于没加吗

  P.S.当边权有负数时求直径不能用两边dfs,而应该用dp。WA了好久qwq。。。于是我第一遍求直径时用的两边dfs,第二遍用的dp。。。

代码:

  1. #include<bits/stdc++.h>
  2.  
  3. using namespace std;
  4.  
  5. const int maxn=;
  6. int dep[maxn],fa[maxn],head[maxn],cnt=,root,ans,n,k,mdep[maxn],maxx;
  7.  
  8. struct ed{
  9. int next,to,w;
  10. }e[maxn<<];
  11.  
  12. void add(int u,int v){
  13. e[++cnt]=(ed){head[u],v,},head[u]=cnt;
  14. e[++cnt]=(ed){head[v],u,},head[v]=cnt;
  15. }
  16.  
  17. void dfs(int now,int f){
  18. fa[now]=f;mdep[now]=;
  19. for(int i=head[now];i;i=e[i].next){
  20. int tt=e[i].to;
  21. if(tt==f) continue;
  22. dep[tt]=dep[now]+e[i].w;
  23. dfs(tt,now); mdep[now]=max(mdep[now],mdep[tt]+e[i].w);
  24. }
  25. }
  26.  
  27. void dp(int now,int f){
  28. int fm=-,sm=-;
  29. for(int i=head[now];i;i=e[i].next){
  30. int tt=e[i].to;
  31. if(tt==f) continue;
  32. if(mdep[tt]+e[i].w>sm){
  33. if(mdep[tt]+e[i].w>fm)
  34. swap(fm,sm),fm=mdep[tt]+e[i].w;
  35. else
  36. sm=mdep[tt]+e[i].w;
  37. }
  38. dp(tt,now);
  39. }
  40. maxx=max(maxx,fm+sm);
  41. maxx=max(maxx,fm);
  42. maxx=max(maxx,sm);
  43. }
  44.  
  45. int main(){
  46. scanf("%d%d",&n,&k);
  47. int u,v;
  48. for(int i=;i<n;i++)
  49. scanf("%d%d",&u,&v),add(u,v);
  50. dfs(,);root=;
  51. for(int i=;i<=n;i++)
  52. if(dep[i]>dep[root])
  53. root=i;
  54. dep[root]=,dfs(root,);
  55. for(int i=;i<=n;i++)
  56. if(dep[i]>dep[root])
  57. root=i;
  58. ans=(n-)*-dep[root]+;
  59. if(k==){
  60. while(fa[root])
  61. for(int i=head[root];i;i=e[i].next){
  62. int tt=e[i].to;
  63. if(tt==fa[root]){
  64. e[i].w=e[i^].w=-;
  65. root=fa[root];break;
  66. }
  67. }
  68. dep[]=;maxx=-0x3f3f3f3f;
  69. dfs(,);dp(,);
  70. ans-=maxx-;
  71. }
  72. printf("%d",ans);
  73. return ;
  74. }

BZOJ 1912: [Apio2010]patrol 巡逻 (树的直径)(详解)的更多相关文章

  1. bzoj 1912 : [Apio2010]patrol 巡逻 树的直径

    题目链接 如果k==1, 显然就是直径. k==2的时候, 把直径的边权变为-1, 然后在求一次直径. 变为-1是因为如果在走一次这条边, 答案会增加1. 学到了新的求直径的方法... #includ ...

  2. bzoj 1912: [Apio2010]patrol 巡逻【不是dp是枚举+堆】

    我是智障系列.用了及其麻烦的方法= =其实树形sp就能解决 设直径长度+1为len(环长) 首先k=1,直接连直径两端就好,答案是2*n-len 然后对于k=2,正常人的做法是树形dp:先求直径,然后 ...

  3. bzoj 1912: [Apio2010]patrol 巡逻

    呵呵呵呵呵呵,自己画图,大概半个小时,觉的连上边会成环(是不是该交仙人掌了??)然后求环不重合部分最大就好了, 结果写了一坨DP,最后写不下去了,再次扒了题解. 发现我真的是个sb. k==1,直接是 ...

  4. 【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)

    1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1034  Solved: 562[Submit][St ...

  5. 【BZOJ】1912: [Apio2010]patrol 巡逻(树的直径)

    题目 传送门:QWQ 分析 $ k=1 $ 时显然就是树的直径 $ k=2 $ 时怎么做呢? 做法是把一开始树的直径上的边的边权改成$ -1 $,那么当我们第二次用这些边做环时就抵消了一开始的贡献. ...

  6. BZOJ 1912:[Apio2010]patrol 巡逻(树直径)

    1912: [Apio2010]patrol 巡逻 Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ ...

  7. [Apio2010]patrol 巡逻

    1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 2541  Solved: 1288[Submit][S ...

  8. 【BZOJ1912】[Apio2010]patrol 巡逻 树形DP

    [BZOJ1912][Apio2010]patrol 巡逻 Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示 ...

  9. AVL树平衡旋转详解

    AVL树平衡旋转详解 概述 AVL树又叫做平衡二叉树.前言部分我也有说到,AVL树的前提是二叉排序树(BST或叫做二叉查找树).由于在生成BST树的过程中可能会出现线型树结构,比如插入的顺序是:1, ...

随机推荐

  1. [转帖]Linux:cut命令详解

    Linux:cut命令详解 https://www.cnblogs.com/Spiro-K/p/6361646.html cut -f cut -f -d cut -c1- 这三个命令好像最常见, 记 ...

  2. Sigma Function

    做完这道题,我明白了人生的一个巨大道理,那就是: 其他题研究两下,做出来几百行.数论码字前研究半天,做出来十几二十行.做完特别没有成就感... 首先说下这题题意:首先,定义一个函数f[n],即为他所有 ...

  3. 转《vue引入第三方js库》

    一.绝对路径直接引入,全局可用 二.绝对路径直接引入,配置后,import 引入后再使用 三.webpack中配置 alias,import 引入后再使用 四.webpack 中配置 plugins, ...

  4. linux之硬盘管理fdisk

    1.首先我们用虚拟机模拟服务器加入一块新的硬盘,硬盘容量入下图5GB硬盘. 2.首先我们要对它进行分区,我们从上面知道需要分区格式化的是第二块硬盘. 3.输入n回车是新建分区,p是主分区(只能建立4个 ...

  5. lombok 使用 Idea

    Lombok 是一种 Java™ 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO).它通过注解实现这一目的.import lombok.Getter;i ...

  6. linux 查看网段内所有IP

    如有转载,不胜荣幸.http://www.cnblogs.com/aaron-agu/ 方法一: nmap –nsP 192.168.1.0/24 #从192.168.1.0到192.168.1.25 ...

  7. CountDownLatch(三)

    CountDownLatch简介 (1)用于解决什么问题? 在并发编程的场景中,最常见的一个case是某个任务的执行,需要等到多个线程都执行完毕之后才可以进行,CountDownLatch可以很好解决 ...

  8. 【python练习题】程序17

    #题目:输入一行字符,分别统计出其中英文字母.空格.数字和其它字符的个数. s = input('请输入字符串:') alf = 0 space = 0 digi = 0 other = 0 for ...

  9. 基于junit的单元测试类编写

    首先定义抽象类BaseTest package com.geostar.gfstack.operationcenter.common.util; import com.google.gson.Gson ...

  10. java excel Workbook API

    此文摘自:http://blog.sina.com.cn/zenyunhai 1. int getNumberOfSheets() 获得工作薄(Workbook)中工作表(Sheet)的个数,示例: ...