这个题其实是树的直径
过程就是一个深搜的过程, 考虑在走过时加环可以有一条边不用走
看这个图,显然由2出发的最长边为2-1-3-5-8,所以在2,8之间加一条边
最短
设直径为L, 答案就是2(n-1)-L+1
但是,这只是k=1的情况,k=2时呢?
再形成了一个环。
但是两个环会有重叠部分!
如果继续按刚才的方法巡逻,就必须再走一遍。
这个时候重叠的部分就会巡逻两边hh
所以要在跑完一遍最初的树上直径后再将直径上的边权取反。
再求直径L2
ans = 2(n - 1) - (L1-1) - (L2-1) = 2n-L1-L2
时间复杂度O(n)
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int head[N],ver[N],Next[N],tot = 1,k,n;
int max1, q, fa[N], v[N], ans, sum, d[N];
void add(int x,int y) {
ver[tot] = y;
Next[tot] = head[x];
head[x] = tot++;
}
int x1;
void dfs1(int x,int f,int ans1) {
if (ans1 > max1) {
max1 = ans1;
x1 = x;
}
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if (y == f) continue;
dfs1(y,x,ans1 + 1);
}
}
void dfs2(int x,int f,int ans1) {
if (ans1 > max1) {
max1 = ans1;
q = x;
}
fa[x] = f;
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if (y == f) continue;
dfs2(y,x,ans1 + 1);
}
}
void dfs3(int x,int f) {
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if (y == f) continue;
dfs3(y,x);
int dist = 1;
if (v[x] && v[y]) dist = -1;
ans = max(ans,d[x] + d[y] + dist);
d[x] = max(d[x],d[y] + dist);
}
}
int main() {
scanf("%d%d",&n,&k);
for (int i = 1; i < n; i++) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1,0,0);
max1 = 0;
dfs2(x1,0,0);
if (k == 1) {
printf("%d\n",2 * (n - 1) - max1 + 1);
return 0;
}
while (q != 0) {
v[q] = 1;
q = fa[q];
}
dfs3(1,0);
sum = 2 * (n - 1) - max1 + 1;
printf("%d\n",sum - ans + 1);
return 0;
}