树的直径初探+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. 巡警车每 ...
随机推荐
- 【转载】C#扫盲之:==/Equals /ReferenceEquals 异同的总结,相等性你真的知道吗?
1.前言 == Equals ReferenceEquals 三个相等性测试,是.NET提供给程序员使用的三个方法,他们之间有什么联系和区别,你真的仔细研究过?虽然之前也多多少少知道一点,但是有时候又 ...
- Mesa (computer graphics)
http://en.wikipedia.org/wiki/Mesa_(computer_graphics) Mesa (computer graphics) From Wikipedia, the f ...
- TinyXML的使用
TinyXML TinyXML是一个简单的小型C ++ XML解析器,可以轻松集成到其他程序中. 它能做什么: 简而言之,TinyXML解析XML文档,并从可以读取,修改和保存的文档对象模型(DOM) ...
- hiho1079 线段树区间改动离散化
题目链接: hihocoder1079 代码: #include<iostream> #include<cstdio> #include<cstring> #inc ...
- T-SQL查询进阶--变量
概述 变量对于一种语言是必不可少的一部分,当然,对于T-SQL来讲也是一样.在简单查询中,往往很少用到变量,但无论对于复杂的查询或存储过程中,变量都是必不可少的一部分. 变量的种类 在T-SQL中,变 ...
- jvm 调优(2)垃圾回收算法
可以从不同的的角度去划分垃圾回收算法: 按照基本回收策略分 引用计数(Reference Counting): 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数. ...
- YTU 2420: C语言习题 不等长字符串排序
2420: C语言习题 不等长字符串排序 时间限制: 1 Sec 内存限制: 128 MB 提交: 460 解决: 239 题目描述 在主函数中输入n(n<=10)个不等长的字符串.用另一函 ...
- YTU 2457: 很简单的一道题
2457: 很简单的一道题 时间限制: 1 Sec 内存限制: 128 MB 提交: 261 解决: 80 [提交][状态][讨论版] 题目描述 有一个简单的函数数学公式,如下 输入 重复输入多组 ...
- hdu 1400 Mondriaan's Dream 解题报告
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1400 题目意思:给出一个h * w的 大 矩形,需要用 1 * 2 的砖块去填充这个大矩形,问填充的方 ...
- WAS:修改jsp编译器用JDK5.0
问题现象: 今天有现场反映,访问应用的个别页面报错,报错内容如下: 于是先查看其他现场,都是好的:根据报错信息,提示的意思是jsp解析不了. 结合上面2个情况,排除代码问题,应该是现场WAS环境问 ...