【洛谷 P3629】 [APIO2010]巡逻 (树的直径)
容易发现,当加一条边时,树上会形成一个环,这个环上的每个点都是只要走一次的,也就是说我们的答案减少了这个环上点的个数,要使答案最小,即要使环上的点最多,求出直径\(L\),则答案为\(2(n-1)-L+1\)。
当加两条边时,同样会形成一个新环,但这个新环可能和第一个环有交点,而这些交点仍是要走两次的,所以我们要让交点的个数尽可能小,所以,把原直径上的所有边权取反,代表若取了这条边,答案会增大那么多,然后再求一次树的直径\(L_1\),则答案为\(2(n-1)-L+1-L_1+1=2n-L-L_1\)。
注意,第二次求直径不能用两边\(DFS/BFS\)来求,因为树中有负权边,直接跑答案显然是错的,所以我们要用树形\(DP\)求直径。
#include <cstdio>
const int MAXN = 5000010;
namespace IO{
inline int read(){
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
return s * w;
}
}using namespace IO;
namespace G{
struct Edge{
int next, to, dis;
}e[MAXN << 1];
int head[MAXN], num;
inline void Add(int from, int to, int dis){
e[++num].to = to;
e[num].dis = dis;
e[num].next = head[from];
head[from] = num;
}
}using namespace G;
int n, k, s, t;
int a, b;
int pre[MAXN];
int Max = 0;
inline int max(int a, int b){
return a > b ? a : b;
}
void dfs(int u, int fa, int dep){
if(dep > Max && fa) s = u, Max = dep;
for(int i = head[u]; i; i = e[i].next)
if(e[i].to != fa)
dfs(e[i].to, u, dep + e[i].dis);
}
void DFS(int u, int fa, int dep){
if(dep > Max) t = u, Max = dep;
for(int i = head[u]; i; i = e[i].next)
if(e[i].to != fa)
pre[e[i].to] = u, DFS(e[i].to, u, dep + e[i].dis);
}
int d[MAXN], ans = -2147483647;
void dp(int u, int fa){
for(int i = head[u]; i; i = e[i].next)
if(e[i].to != fa){
dp(e[i].to, u);
ans = max(ans, d[u] + d[e[i].to] + e[i].dis);
d[u] = max(d[u], d[e[i].to] + e[i].dis);
}
}
int main(){
n = read(); k = read();
for(int i = 1; i < n; ++i){
a = read(); b = read();
Add(a, b, 1); Add(b, a, 1);
}
Max = -2147483647; dfs(1, 0, 0);
Max = -2147483647; DFS(s, 0, 0);
if(k == 1){
printf("%d\n", (n << 1) - 1 - Max);
return 0;
}
int now = t;
while(now != s){
for(int i = head[now]; i; i = e[i].next)
if(e[i].to == pre[now]){
e[i].dis = -1;
break;
}
now = pre[now];
}
dp(t, 0);
printf("%d\n", (n << 1) - Max - ans);
return 0;
}
【洛谷 P3629】 [APIO2010]巡逻 (树的直径)的更多相关文章
- 洛谷 P3629 [APIO2010]巡逻 解题报告
P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...
- 洛谷P3629 [APIO2010]巡逻(树的直径)
如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\). 考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍.这时选择连接树的直径的两个端点显然是最优的. 难就 ...
- [洛谷P3629] [APIO2010]巡逻
洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...
- 洛谷 P3629 [APIO2010]巡逻
题目在这里 这是一个紫题,当然很难. 我们往简单的想,不建立新的道路时,从1号节点出发,把整棵树上的每条边遍历至少一次,再回到1号节点,会恰好经过每条边两次,路线总长度为$2(n-1)$,根据树的深度 ...
- BZOJ1912或洛谷3629 [APIO2010]巡逻
一道树的直径 BZOJ原题链接 洛谷原题链接 显然在原图上路线的总长为\(2(n-1)\). 添加第一条边时,显然会形成一个环,而这条环上的所有边全部只需要走一遍.所以为了使添加的边的贡献最大化,我们 ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- 【BZOJ2830/洛谷3830】随机树(动态规划)
[BZOJ2830/洛谷3830]随机树(动态规划) 题面 洛谷 题解 先考虑第一问. 第一问的答案显然就是所有情况下所有点的深度的平均数. 考虑新加入的两个点,一定会删去某个叶子,然后新加入两个深度 ...
- 树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925
题目传送门 我们先来介绍一个概念:树的直径. 树的直径:树中最远的两个节点间的距离.(树的最长链)树的直径有两种方法,都是$O(N)$. 第一种:两遍bfs/dfs(这里写的是两遍bfs) 从任意一个 ...
- 洛谷 [P3629] 巡逻
树的直径 树的直径有两种求法 1.两遍 dfs 法, 便于输出具体方案,但是无法处理负权边 2.DP 法,代码量少,可以处理负权边 #include <iostream> #include ...
随机推荐
- 形象的理解Strong和Weak
Strong Weak
- 【APUE】Chapter15 Interprocess Communication
15.1 Introduction 这部分太多概念我不了解.只看懂了最后一段,进程间通信(IPC)内容被组织成了三个部分: (1)classical IPC : pipes, FIFOs, messa ...
- 6.爬虫 requests库讲解 总结
requests库的总结: 用ProcessOn根据前面的几节内容做了个思维导图:
- parity的使用
parity --chain dev --port 8045 ps aux | grep "parity" ps -elf | grep "pari"
- BST插入与查找
B树: 二叉查找树,所有左节点都比父节点要小,所有右节点都比父节点要大.查找,插入的时间复杂度为O(logn) public class BTreeTest { public static int[] ...
- 软工实践Beta冲刺(5/7)
队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记 ...
- 阿里云服务器 linux下载 jdk
直接从本地下载包上传比较慢.直接在服务器上下载安装包: 1.进入orcle官网; 2.选择需要下载的版本,下载需要同意orcle协议, 3.点击下载,获取到下载请求的cookie, 复制所有cooki ...
- 健康领域今年开始井喷了,养老地产和私人医生这两个领域目测成为下一轮BAT在健康领域布局的竞争方向
医疗行业做了六年多的时间,今年到了井喷的阶段,腾讯先是入股了丁香园,然后又一亿美金融资挂号网,春雨医生获得5000万美元的C轮融资,这是要上市的节奏.. 从互联网战略上,健康网和医疗网都是做资料刚开始 ...
- 数组中键key相等时,后面的值覆盖前面的值
<?php $arr[]='abc'; $arr[]='; $arr[]='; $arr[]='; var_dump($arr); 结果;
- css如何选择相同class下的第一个class元素和最后一个元素?
如图,如果像选择类名为 class="exerciseInfo" 中的第一个和最后一个div,做法如下: 选择第一个类名: .exerciseInfo: nth-of-type ...