• 题目链接:

    https://www.luogu.org/problemnew/show/P3629

  • 分析

    最近被众多dalao暴虐,这道题傻逼地调了两天才知道错哪

    不过这题比较良心给你一个容易发现性质的图

    • 不修路时

      每条路走两次可知需要走\(2(N-1)\)步

    • \(K=1\)

      送分给你,直接\(O(N)\)求直径,若直径长为\(L\),由于新加路还要走一步,少走了\(L-1\)步

    • \(K=2\)

      如果还是用求直径的方法来求发现不太对,与原来直径重叠那部分又要多走一遍

      ,于是不妨把原来直径边权取反再求一边直径,若长为L',因为已经减去重叠部分还是少走\((L'-1)\)步,答案就为\(2(N-1)-L-L'\)

  • 注意

    好象直径取反后不能简单地用dfs求直径,因为在第一次找最远点时可能得到一个错误的答案,于是就用DP来求,顺便学了一下DP求直径

  • DP求树的直径

    \(D[v_i]\)表示在以\(v_i\)为根子树内走到的最大深度

    转移:\(v_1,v_2...v_k\)是\(v_i\)子树内节点 \(D[i]=max_{1<=j<=k}(D[v_j]+edge(v_i,v_j))\)

    若\(v_a,v_b\)是\(v_x\)子树内两节点,树的直径可以看作由四部分组成:

    \(D[v_a]+edge(v_a,v_x)+edge(v_x,v_b)+D[v_b]\)

    具体看代码实现

  • 代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <map>
#include <queue>
#include <algorithm>
#define ri register int
#define ll long long
using namespace std;
const int maxn=100005;
const int inf=0x7fffffff;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;
return ;
}
int n,k;
struct Edge{
int ne,to,dis;
}edge[maxn<<2];
int num_edge=-1,h[maxn];
int s,t;
inline void add_edge(int f,int t){
edge[++num_edge].ne=h[f];
edge[num_edge].to=t;
edge[num_edge].dis=1;
h[f]=num_edge;
}
int mx=-inf,vis[maxn];
void dfs_1(int fa,int cur,int cnt){
for(ri i=h[cur];i!=-1;i=edge[i].ne){
if(edge[i].to!=fa){
dfs_1(cur,edge[i].to,cnt+1);
}
}
if(cnt>mx){
mx=cnt,t=cur;
}
return ;
}
int pre[maxn],dmet[maxn],tot=0,ex=0;
void dfs_2(int fa,int cur,int cnt){
for(ri i=h[cur];i!=-1;i=edge[i].ne){
if(edge[i].to!=fa){
dfs_2(cur,edge[i].to,cnt+1);
pre[edge[i].to]=cur;
}
}
if(cnt>mx){
mx=cnt,s=cur;
}
return ;
}
int diameter;
void dfs_3(int fa,int cur,int cnt){
if(cur==t){
diameter=cnt;
return ;
}
for(ri i=h[cur];i!=-1;i=edge[i].ne){
int v=edge[i].to;
if(vis[v]&&v!=fa){
dfs_3(cur,v,cnt+1);
edge[i].dis=-1;
edge[i^1].dis=-1;
}
}
return ;
}
int d[maxn];
void dp(int fa,int now){
for(ri i=h[now];i!=-1;i=edge[i].ne){
int v=edge[i].to;
if(v==fa)continue;
dp(now,v);
mx=max(mx,d[now]+d[v]+edge[i].dis);//上一次循环已更新一次d[now]
d[now]=max(d[now],d[v]+edge[i].dis);
}
return ;
}
int main(){
int x,y;
read(n),read(k);
memset(h,-1,sizeof(h));
for(ri i=1;i<n;i++){
read(x),read(y);
add_edge(x,y);
add_edge(y,x);
}
memset(vis,0,sizeof(vis));
dfs_1(0,1,0);
mx=-inf;
dfs_2(0,t,0);
int tmp=s;
while(tmp!=t){
vis[tmp]=1;
dmet[++tot]=tmp;
tmp=pre[tmp];
}
dmet[++tot]=t,vis[t]=1;
dfs_3(0,s,0);
//cout<<s<<' '<<t<<' '<<diameter<<endl;
/*-------*/
if(k==2){
mx=0;
dp(0,1);
int diameter_2=mx;
//cout<<mx<<endl;
if(mx<0)diameter_2=0;
printf("%d\n",2*n-diameter-diameter_2);
}
else{
printf("%d\n",2*(n-1)-(diameter-1));
}
return 0;
}

luogu题解 P3629 【[APIO2010]巡逻】树的直径变式的更多相关文章

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

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

  2. 洛谷 P3629 [APIO2010]巡逻 解题报告

    P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...

  3. 树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925

    题目传送门 我们先来介绍一个概念:树的直径. 树的直径:树中最远的两个节点间的距离.(树的最长链)树的直径有两种方法,都是$O(N)$. 第一种:两遍bfs/dfs(这里写的是两遍bfs) 从任意一个 ...

  4. 洛谷P3629 [APIO2010]巡逻(树的直径)

    如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\). 考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍.这时选择连接树的直径的两个端点显然是最优的. 难就 ...

  5. [洛谷P3629] [APIO2010]巡逻

    洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...

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

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

  7. P3629 [APIO2010]巡逻

    题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一个村庄.每条道 ...

  8. 洛谷 P3629 [APIO2010]巡逻

    题目在这里 这是一个紫题,当然很难. 我们往简单的想,不建立新的道路时,从1号节点出发,把整棵树上的每条边遍历至少一次,再回到1号节点,会恰好经过每条边两次,路线总长度为$2(n-1)$,根据树的深度 ...

  9. 题解 BZOJ 1912 && luogu P3629 [APIO2010]巡逻 (树的直径)

    本来抄了篇题解,后来觉得题解都太不友好(我太菜了),一气之下自己打...一打打到第二天QAQ 首先什么边也不加时,总路程就是2*(n-1) 考虑k=1的时候,答案显然是2*(n-1)-直径+1=2*n ...

随机推荐

  1. UML期末复习题——2.9:UML Deployment Diagram

    附加题:部署图 重要概念: 1. 部署图 部署图表示的是,如何将具体的软件制品(例如可执行文件)分配到计算节点(具有处理服务的某种事物)上.部署图表示了软件元素在物理架构上的部署,以及物理元素之间的通 ...

  2. kotlin 类的继承

    与Java不同,kotlin 使用冒号,而Java 中使用extends, 注意冒号后面需要调用夫类的构造器.属于单继承,使用open 关键字允许继承class package loaderman.d ...

  3. Spring事务管理4-----声明式事务管理(2)

    声明式事务管理  基于AspectJ的 XML 方式配置 通过对事务管理器TransactionManager配置通知(增强),然后再配置切点和切面,详细见applicationContext.xml ...

  4. Linux下如何启用MySQL数据库远程访问

    远程连接MySQL出于安全考虑,一般都关闭了远程访问,但有时候需要提供远程访问数据库的服务,下面我们快速学习下: 第一步:修改my.cnf文件使用文本编辑器去编辑MySQL服务器的配置文件my.cnf ...

  5. css解决fixed布局不会出现滚动条的问题

  6. asp.net网站访问时不能显示页面

    web => 属性 => web => servers =>create virtual directory

  7. iOS-自定义导航控制器

    BasicNavigationViewController:UINavigationViwController /* 隐藏导航底部线条 */ -(void)viewDidLoad{    [super ...

  8. springMVC配置文件学习

    spring配置文件分为dao层,web层,service层,三层配置 这三层配置中, dao层对应数据库的配置:进行数据库相关和model实体类的配置 web层对应controller包中配置:设置 ...

  9. 小程序签名MD5加密

    最近小程序需求一个签名加密,要把请求参数按键值排序并转化为字符串,然后进行MD5加密. //时间戳 var timestamp = (Date.parse(new Date()))/1000;//签名 ...

  10. sqlmap工具介绍

    工具介绍 sqlmap是一个自动化的SQL注入工具,其主要功能是扫描,发现并利用给定的URL的SQL注入漏洞,目前支持的数据库是MySQL, Oracle, PostgreSQL, Microsoft ...