• 题目链接:

    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. js闭包小实验

    js闭包小实验 一.总结 一句话总结: 闭包中引用闭包外的变量会使他们常驻内存 function foo() { var i=0; return function () { console.log(i ...

  2. vue实现穿梭框效果

    vue实现穿梭框效果 一.总结 一句话总结: 用两个数组分别记录左右框框里面的值,用两个数组绑定checkbox,用来记录选中的checkbox值,根据选中的checkbox的值实现删除增加即可 1. ...

  3. Django中的 返回json对象的方式

    在返回json对象的几种方式: 1 from django.shortcuts import render, HttpResponse # Create your views here. from d ...

  4. Java同步数据结构之LinkedBlockingDeque

    前言 前面介绍完了队列Queue/BlockingQueue的实现类,接下来介绍双端队列Deque/BlockingDeque的实现类之一LinkedBlockingDeque,它是一种基于链表的可选 ...

  5. php+上传文件夹

    一提到大文件上传,首先想到的是啥??? 没错,就是修改php.ini文件里的上传限制,那就是upload_max_filesize.修改成合适参数我们就可以进行愉快的上传文件了.当然啦,这是一般情况下 ...

  6. 跨域form下载方式 批量下载

    downloadFileForm:function(fid) { var url = "https://file.xxxx.com/fileDownload.do"; var in ...

  7. asp.net mvc 中Html.ValidationSummary显示html

    @if (!ViewData.ModelState.IsValid) { <div>@Html.Raw(HttpUtility.HtmlDecode(Html.ValidationSumm ...

  8. Path环境变量的作用

    作用: 当我们要求系统运行一个程序(例如a.exe)而没有告诉它程序所在的完整路径时,系统会先在当前目录寻找是否存在a.exe,如果找到,直接运行:如果没有找到,会去path路径下面找.设置path, ...

  9. 启动Nginx 出现 nginx: [emerg] unknown directive "锘?user" 错误

    出现这种情况 一般是修改配置文件 nginx.conf 造成的 如果你修改文件后出现 那基本上就是这个原因 启动不了 重新打开 改为UTF-8 无BOM编码

  10. C#实现排列、组合

    排列组合的概念 排列:从n个不同元素中取出m(m≤n)个元素,按照一定的顺序排成一列,叫做从n个元素中取出m个元素的一个排列(Arrangement). 组合:从m个不同的元素中,任取n(n≤m)个元 ...