树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925
我们先来介绍一个概念:树的直径。
树的直径:树中最远的两个节点间的距离。(树的最长链)
树的直径有两种方法,都是$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的更多相关文章
- 题解 BZOJ 1912 && luogu P3629 [APIO2010]巡逻 (树的直径)
本来抄了篇题解,后来觉得题解都太不友好(我太菜了),一气之下自己打...一打打到第二天QAQ 首先什么边也不加时,总路程就是2*(n-1) 考虑k=1的时候,答案显然是2*(n-1)-直径+1=2*n ...
- 洛谷 P3629 [APIO2010]巡逻 解题报告
P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...
- P3629 [APIO2010] 巡逻 (树的直径)
(这道题考察了求直径的两种方法......) 在原图中,每条边要经过两次,增加1条后,形成了一个环,那么环上的边只需要经过一次了(大量画图分析得),再增加一条又会形成一个环,如果这两个环有重叠,重叠部 ...
- [洛谷P3629] [APIO2010]巡逻
洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...
- 洛谷P3629 [APIO2010]巡逻(树的直径)
如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\). 考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍.这时选择连接树的直径的两个端点显然是最优的. 难就 ...
- P3629 [APIO2010]巡逻
题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一个村庄.每条道 ...
- 洛谷 P3629 [APIO2010]巡逻
题目在这里 这是一个紫题,当然很难. 我们往简单的想,不建立新的道路时,从1号节点出发,把整棵树上的每条边遍历至少一次,再回到1号节点,会恰好经过每条边两次,路线总长度为$2(n-1)$,根据树的深度 ...
- Luogu 3629 [APIO2010]巡逻
先考虑$k = 1$的情况,很明显每一条边都要被走两遍,而连成一个环之后,环上的每一条边都只要走一遍即可,所以我们使这个环的长度尽可能大,那么一棵树中最长的路径就是树的直径. 设直径的长度为$L$,答 ...
- 【题解】P3629 [APIO2010]巡逻
link 题意 有 \(n\) 个村庄,编号为 \(1, 2, ..., n\) .有 \(n – 1\) 条道路连接着这些村 庄,从任何一个村庄都可以到达其他任一个村庄.道路长度均为 1. 巡警车每 ...
随机推荐
- Eclipse - 循环cin的输出怎样终止
循环cin的输出怎样终止 本文地址: http://blog.csdn.net/caroline_wendy Eclipse中, 使用CDT编写C++代码时, 循环(while)cin输入程序, 须要 ...
- erlang 中文编码显示乱码问题
许久没做erlang开发了,近期有网友问到erlang的问题.就抽时间看下.问题是这种.模块有中文.将中文直接打印出来.shell下显示会出现乱码.但假设先将中文转成binary.就行正常显示出来. ...
- android编译遇到问题修改
(注意要确定安装了jdk) 第一步: cd lichee; ./build.sh -p sun5i_elite -k 3.0 (apt-get install uboot-mkimage需要安装 ...
- 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 ...
- JAVA运行时异常及常见的5中RuntimeExecption
最近在抽时间看面试题,很多面试题都提出了写出java常见的5个运行时异常.现在来总结一下, java运行时异常是可能在java虚拟机正常工作时抛出的异常. java提供了两种异常机制.一种是运行时异常 ...
- C# 给窗体添加事件
1.https://zhidao.baidu.com/question/588485101.html
- 数组、栈、堆(java基础知识五)
1.数组概述.定义格式 * A:数组概念 数组是存储同一种数据类型多个元素的集合.也可以看成是一个容器. 数组既可以存储基本数据类型,也可以存储引用数据类型. * B:数组定义格式 格式1:数据类型[ ...
- 1Q84 BOOK 1-3 笔记
1Q84 BOOK 1-3 村上春树 18个笔记 ◆ 第2章 青豆 不要被外表迷惑 >> 在这三十年的人生中,究竟听过多少次同样的台词?为了这个姓氏,曾经多少次遭人调侃?如果不 ...
- 基于HALCON的模板匹配方法总结 (转)
很早就想总结一下前段时间学习HALCON的心得,但由于其他的事情总是抽不出时间.去年有过一段时间的集中学习,做了许多的练习和实验,并对基于HDevelop的形状匹配算法的参数优化进行了研究,写了一篇& ...
- JS截取与分割字符串常用技巧总结
本文实例讲述了JS截取与分割字符串的常用方法.分享给大家供大家参考,具体如下: JS截取字符串可使用 substring()或者slice() 函数:substring() 定义:substring( ...