先考虑$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. POJ 3071 Football (概率DP)

    概率dp的典型题.用dp[j][i]表示第j个队第i场赢的概率.那么这场要赢就必须前一场赢了而且这一场战胜了可能的对手.这些都好想,关键是怎么找出当前要算的队伍的所有可能的竞争对手?这个用异或来算,从 ...

  2. git常用命令收藏

    git init //初始化本地git环境 git clone XXX//克隆一份代码到本地仓库 git pull //把远程库的代码更新到工作台 git pull --rebase origin m ...

  3. WC2018 即时战略

    交互题 一棵树,一开始只有 1 号点是已知的,其他的都是未知的,你可以调用函数 explore(x,y) ,其中 x 必须是已知的,函数会找到 x 到 y 路径上第二个点,并把它标成已知,求最小步数使 ...

  4. Number Sequence (KMP的应用)

    个人心得:朴素代码绝对超时,所以要用到KMP算法,特意了解了,还是比较抽象,要多体会 Given two sequences of numbers : a11, a22, ...... , aNN, ...

  5. [Luogu3852][TJOI2007]小朋友

    luogu 题意 求弦图的最大独立集. sol 按照完美消除序列一个个贪心选即可. code #include<cstdio> #include<algorithm> #inc ...

  6. Unity Shader实现描边效果

    http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...

  7. [调试日志]用php函数var_export把多维数组file_put_contents写入并打印到日志,以方便调试之多维数组,用php5中的var_export函数示例,顺带介绍http_build_query(转)

    一行解决写入日志: file_put_contents("/tmp/jack.txt", var_export($layReturnArr,TRUE),FILE_APPEND); ...

  8. 使用批处理自动发布IIS站点,基于IIS7及以上

    经过研究,终于使用批处理解决了站点发布步骤多的问题. 完整批处理如下: @set "sitePath=%~dp0" @echo 新建程序池 @C:\Windows\System32 ...

  9. Django基础(五)

    Django admin 自带的验证: from django.contrib.auth.decorators import login_required from django.contrib.au ...

  10. Tortoisesvn 如何在资源管理器中断开连接

    你在这个文件夹下打开“工具—文件夹选项—查看”,勾选“显示隐藏的文件夹”选项,可以看到在SVN所在的文件夹下面, 有一个.svn文件夹,把它删除了,刷新一下就可以了.