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

设直径的长度为$L$,答案就是$2(n - 1) - L + 1 = 2n - L - 1$。

考虑$k = 2$的情况,发现第一条边一定还是要把直径练成一个环,而第二条边是要再求一个类似于直径的东西,具体来说,可以把原来直径(记为$L_{1}$)上的每一条边的边权取为$-1$,然后再求一遍直径(记为$L_{2}$),这样子的话答案就是$2(n - 1) - (L_{1} - 1) - (L_{2} - 1) = 2n - L_{1} - L_{2}$。发现这样做之后如果第二条直径上包含着第一条直径上的部分,那么重叠的部分就被重新加了回来,所以这样子求出来的答案就是最后的答案。

由于可以在同一个点连边,那么$L_{2}$至少要为$0$。

注意第二次求直径的时候要使用树形$dp$,两次$bfs$的方法会挂,因为边带负权之后会相当于把之前带正权的边的贡献减掉,所以第一次求出来的一端并不一定是直径的一个端点。

时间复杂度$O(n)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std; const int N = 1e5 + ;
const int inf = << ; int n, m, tot = , head[N];
int root, eid[N], dis[N], ans = ;
int f[N], d[N]; struct Edge {
int to, nxt, val;
} e[N << ]; inline void add(int from, int to) {
e[++tot].to = to;
e[tot].val = ;
e[tot].nxt = head[from];
head[from] = tot;
} inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > ''|| ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} inline void chkMax(int &x, int y) {
if(y > x) x = y;
} void dfs(int x, int fat) {
dis[x] = dis[fat] + ;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
eid[y] = i ^ ;
dfs(y, x);
}
} void dfs2(int x, int fat) {
bool flag = ;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
flag = ;
dfs2(y, x);
chkMax(ans, d[y] + e[i].val + d[x]);
chkMax(d[x], d[y] + e[i].val);
}
if(!flag) d[x] = ;
} int main() {
read(n), read(m);
for(int x, y, i = ; i < n; i++) {
read(x), read(y);
add(x, y), add(y, x);
} dis[] = -;
dfs(, );
dis[root = n + ] = -inf;
for(int i = ; i <= n; i++)
if(dis[i] > dis[root]) root = i; dfs(root, ); /* for(int i = 1; i <= n; i++)
printf("%d ", dis[i]);
printf("\n"); */ if(m == ) {
for(int i = ; i <= n; i++)
chkMax(ans, dis[i]);
printf("%d\n", * n - - ans);
return ;
} int pnt = n + ;
for(int i = ; i <= n; i++)
if(dis[pnt] < dis[i]) pnt = i; for(int x = pnt; x != root; x = e[eid[x]].to)
e[eid[x]].val = e[eid[x] ^ ].val = -; memset(f, , sizeof(f));
memset(d, , sizeof(d));
ans = ;
dfs2(root, ); printf("%d\n", * n - dis[pnt] - ans);
return ;
}

Luogu 3629 [APIO2010]巡逻的更多相关文章

  1. BZOJ1912或洛谷3629 [APIO2010]巡逻

    一道树的直径 BZOJ原题链接 洛谷原题链接 显然在原图上路线的总长为\(2(n-1)\). 添加第一条边时,显然会形成一个环,而这条环上的所有边全部只需要走一遍.所以为了使添加的边的贡献最大化,我们 ...

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

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

  3. 树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925

    题目传送门 我们先来介绍一个概念:树的直径. 树的直径:树中最远的两个节点间的距离.(树的最长链)树的直径有两种方法,都是$O(N)$. 第一种:两遍bfs/dfs(这里写的是两遍bfs) 从任意一个 ...

  4. [luogu P3628] [APIO2010]特别行动队

    [luogu P3628] [APIO2010]特别行动队 题目描述 你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 n 编号,要将他们拆分 成若干特别行动队调入战场.出于默契的考虑,同一支特 ...

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

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

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

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

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

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

  8. luogu题解 P3629 【[APIO2010]巡逻】树的直径变式

    题目链接: https://www.luogu.org/problemnew/show/P3629 分析 最近被众多dalao暴虐,这道题傻逼地调了两天才知道错哪 不过这题比较良心给你一个容易发现性质 ...

  9. [Apio2010] 巡逻

    Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Ou ...

随机推荐

  1. cocos2d-html5 中的性能优化

    游戏开发中,难免会遇到性能瓶颈.图片一多,渲染批次就会直线上升,任何动画都会变得闪动. OpenGL ES优化的问题,主要考虑两个方面:内存存储和运行速度. 2D游戏中的最占内存的就是图片资源,一张图 ...

  2. BRICH

    一.简介 Brich是典型的基于层次的聚类算法.最大的特点就是适合数据量特别大的数据集,处理速度很快,因为该算法扫描一遍数据集. 该算法是利用了一个树状结构来快速聚类,该结构类似平衡B+树.每一个叶子 ...

  3. opencv中VideoCapture和cvCapture有什么区别?

    VideoCapture和cvCapture其实是一样的,你可以去看看源码,VideoCapture其实在内部调用了cvCapture.这是不同 版本的opencv导致的.我接触到的opencv有过一 ...

  4. 基于Python语言使用RabbitMQ消息队列(四)

    路由 在上一节我们构建了一个简单的日志系统.我们能够广播消息给很多接收者. 在本节我们将给它添加一些特性——我们让它只订阅所有消息的子集.例如,我们只把严重错误(critical error)导入到日 ...

  5. Unity3D研究院之Assetbundle的原理(六十一)

    www.xuanyusong.com/archives/2373 Assetbundle 是Unity Pro提供提供的功能,它可以把多个游戏对象或者资源二进制文件封装到Assetbundle中,提供 ...

  6. [转]理解$watch ,$apply 和 $digest --- 理解数据绑定过程

    原文地址:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ 注 这篇博文主要是写给新手的,是给那些刚刚开始 ...

  7. jraiser模块加载执行简要总结

    1 在html文件中,通过require方式来加载指定的入口文件:2 然后通过正则表达式来匹配入口文件中的所有require的依赖文件:注意,此时入口文件已加载完毕,不过,还没执行而已.3 之后逐一通 ...

  8. Hive的JDBC访问

    实现hive查询源码: String driverName = "org.apache.hive.jdbc.HiveDriver"; try { Class.forName(dri ...

  9. BZOJ3170:[TJOI2013]松鼠聚会

    题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=3170 通过分析可以发现,题目所说的两点之间的距离就是切比雪夫距离. 两点之间欧几里得距离:\( ...

  10. bae3.0第二步 添加一个空的django项目

    1.创建django项目: 进入bae应用源码目录(appidd01iud80bg)执行django-admin.py startproject blog2.修改index.py的内容如下所示 #-* ...