题目传送门

我们先来介绍一个概念:树的直径。

树的直径:树中最远的两个节点间的距离。(树的最长链)
树的直径有两种方法,都是$O(N)$。

第一种:两遍bfs/dfs(这里写的是两遍bfs)

从任意一个节点出发,遍历一遍树找到与出发点距离最远的点p。

再从节点p出发,遍历一遍求出与p距离最远的点q。则pq即为直径(其中一个)

但是不能处理负权边。

int bfs(int x)
{
queue<int>q;
memset(d,0x3f,sizeof(d));
memset(pre,,sizeof(pre));
fake=d[];
q.push(x);d[x]=;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(d[v]==fake) q.push(v),pre[v]=i,d[v]=d[u]+;
}
}
int top=x;
for(int i=;i<=n;i++)
if(d[i]>d[top]) top=i;
return top;
}
int get_d()
{
p=bfs();
p=bfs(p);
return d[p];
}

第二种:树形dp

void Treedp(int u)
{
vis[u]=;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(vis[v]) continue;
Treedp(v);
ans=max(ans,f[x]+f[y]+edge[i].val)
f[x]=max(f[x],f[y]+edge[i].val);
}
}

题目大意:给你一棵树,你需要把这棵树上的每条边至少遍历一次,走过一条边的代价是1,现在你可以添加1或2条边,新添的边可且仅可遍历一次,问最小代价是多少。

不加边的时候,答案就是$2*(n-1)$。

加一条新道路后,因为新道路必须经过恰好一次,设$l$,$r$为新建道路的两端,那么从$l$去$r$的时候走新道路,回来的时候走原来的道路。形成了一个环,也就是说其他与$l$,$r$无关的点还是走过两次,而连接$l$,$r$的路径上的边走一次就行了。而我们贪心的选择树中最长的路径,那就是树的直径。设直径为$d$,那么答案就是$2*(n-1)-d+1$。(+1是新建的那条道路)

加两条新道路后,也会形成一个环,但是我们不知道这个环与之前的那个环是不是有重叠关系。若重叠,那么两个环重叠的部分就不会被经过;不重叠还好说。那么我们如何解决?处理重叠情况,我们的目标是使重叠部分恰好经过两次,那么我们可以把第一次的直径上的所有边取反(1变成-1),再在取反了的树上找直径。最终答案就是

$2*(n-1)-(l1-1)-(l2-1)$。

Code

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define maxn 100090 using namespace std; int n,k,tot=,fake,p,ans1,ans2;
int head[maxn],d[maxn],pre[maxn],vis[maxn],f[maxn];
struct node{
int to,next,val;
}edge[maxn*]; void add(int x,int y)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
edge[tot].val=;
} int bfs(int s)
{
queue<int>q;
memset(pre,,sizeof(pre));
memset(d,0x3f,sizeof(d));
fake=d[];
q.push(s);d[s]=;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(d[v]==fake) d[v]=d[u]+edge[i].val,pre[v]=i,q.push(v);
}
}
int top=s;
for(int i=;i<=n;i++) if(d[i]>d[top]) top=i;
return top;
} void get_d()
{
p=bfs();
p=bfs(p);
} void Treedp(int u)
{
vis[u]=;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(vis[v]) continue;
Treedp(v);
ans2=max(ans2,f[u]+f[v]+edge[i].val);
f[u]=max(f[u],f[v]+edge[i].val);
}
} int main()
{
scanf("%d%d",&n,&k);
for(int i=;i<=n-;i++)
{
int x=,y=;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
get_d();
ans1=d[p];
if(k==){printf("%d",*(n-)-d[p]+);return ;}
for(int i=pre[p];i;i=pre[edge[i^].to])
edge[i].val=-,edge[i^].val=-;
Treedp();
printf("%d",*(n-)-(ans1-)-(ans2-));
return ;
}

树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925的更多相关文章

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

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

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

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

  3. P3629 [APIO2010] 巡逻 (树的直径)

    (这道题考察了求直径的两种方法......) 在原图中,每条边要经过两次,增加1条后,形成了一个环,那么环上的边只需要经过一次了(大量画图分析得),再增加一条又会形成一个环,如果这两个环有重叠,重叠部 ...

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

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

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

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

  6. P3629 [APIO2010]巡逻

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

  7. 洛谷 P3629 [APIO2010]巡逻

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

  8. Luogu 3629 [APIO2010]巡逻

    先考虑$k = 1$的情况,很明显每一条边都要被走两遍,而连成一个环之后,环上的每一条边都只要走一遍即可,所以我们使这个环的长度尽可能大,那么一棵树中最长的路径就是树的直径. 设直径的长度为$L$,答 ...

  9. 【题解】P3629 [APIO2010]巡逻

    link 题意 有 \(n\) 个村庄,编号为 \(1, 2, ..., n\) .有 \(n – 1\) 条道路连接着这些村 庄,从任何一个村庄都可以到达其他任一个村庄.道路长度均为 1. 巡警车每 ...

随机推荐

  1. Eclipse - 循环cin的输出怎样终止

    循环cin的输出怎样终止 本文地址: http://blog.csdn.net/caroline_wendy Eclipse中, 使用CDT编写C++代码时, 循环(while)cin输入程序, 须要 ...

  2. erlang 中文编码显示乱码问题

    许久没做erlang开发了,近期有网友问到erlang的问题.就抽时间看下.问题是这种.模块有中文.将中文直接打印出来.shell下显示会出现乱码.但假设先将中文转成binary.就行正常显示出来. ...

  3. android编译遇到问题修改

    (注意要确定安装了jdk) 第一步: cd  lichee; ./build.sh  -p sun5i_elite -k 3.0  (apt-get install uboot-mkimage需要安装 ...

  4. Codeforces Round #422 (Div. 2) A. I'm bored with life 暴力

    A. I'm bored with life     Holidays have finished. Thanks to the help of the hacker Leha, Noora mana ...

  5. JAVA运行时异常及常见的5中RuntimeExecption

    最近在抽时间看面试题,很多面试题都提出了写出java常见的5个运行时异常.现在来总结一下, java运行时异常是可能在java虚拟机正常工作时抛出的异常. java提供了两种异常机制.一种是运行时异常 ...

  6. C# 给窗体添加事件

    1.https://zhidao.baidu.com/question/588485101.html

  7. 数组、栈、堆(java基础知识五)

    1.数组概述.定义格式 * A:数组概念 数组是存储同一种数据类型多个元素的集合.也可以看成是一个容器. 数组既可以存储基本数据类型,也可以存储引用数据类型. * B:数组定义格式 格式1:数据类型[ ...

  8. 1Q84 BOOK 1-3 笔记

    1Q84 BOOK 1-3 村上春树 18个笔记     ◆ 第2章 青豆 不要被外表迷惑   >> 在这三十年的人生中,究竟听过多少次同样的台词?为了这个姓氏,曾经多少次遭人调侃?如果不 ...

  9. 基于HALCON的模板匹配方法总结 (转)

    很早就想总结一下前段时间学习HALCON的心得,但由于其他的事情总是抽不出时间.去年有过一段时间的集中学习,做了许多的练习和实验,并对基于HDevelop的形状匹配算法的参数优化进行了研究,写了一篇& ...

  10. JS截取与分割字符串常用技巧总结

    本文实例讲述了JS截取与分割字符串的常用方法.分享给大家供大家参考,具体如下: JS截取字符串可使用 substring()或者slice() 函数:substring() 定义:substring( ...