每添加一条边就会形成一个环,环上的边就都只用经过一次。所以 \(k=1\) 时候求下树的直径,令直径为 \(l_1\), \(2*(n-1)-l_1+1\) 就是答案了。
接下来我们来考虑 \(k=2\) 的时候。
肯定也是要连一条跟直径类似的路径的,但是可能会有边与之前 \(k=1\) 时连的重复。这时候本来这条边只需经过一次,现在变成了两次。所以,要将 \(k=1\) 时求出的直径上的路径的边权都变成 \(-1\) ,然后再求一遍直径。令这条直径为 \(l_2\) 答案就为 \(2*(n-1) - l_1 + 1 - l_2 + 1\) 化简得 \(2n-l_1-l_2\) 。
这里使用 树形Dp 来求树的直径。
#include <iostream>
#include <cstdio>
#include <cstring>
const int MaxN = 1e5 + 5;
int N, K, L1, L2, Edge, Ans, T1, T2;
int Dp[MaxN], FST[MaxN], W[MaxN], F[MaxN], Vis[MaxN];
struct Linker
{
int to, nxt;
Linker(){}
Linker(int x, int y)
{
to = y;
nxt = FST[x];
}
} E[MaxN << 1];
inline int read()
{
register int x = 0;
register char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
inline void AddEdge(int u, int v)
{
E[++Edge] = Linker(u, v);
FST[u] = Edge;
}
void DP(int x)
{
Vis[x] = 1;
for(int k = FST[x]; k; k = E[k].nxt)
{
int to = E[k].to, w = W[to] == 0 ? 1 : - 1;
if(Vis[to]) continue;
DP(to);
if(Dp[x] + Dp[to] + w > Ans)
{
Ans = Dp[x] + Dp[to] + w;
T1 = F[x];
T2 = to;
}
if(Dp[to] + w > Dp[x])
{
Dp[x] = Dp[to] + w;
F[x] = to;
}
}
}
void paint(int x)
{
if(!x) return;
W[x] = 1;
paint(F[x]);
}
int main()
{
N = read();
K = read();
for(int i = 1; i < N; ++i)
{
int u = read(), v = read();
AddEdge(u, v);
AddEdge(v, u);
}
DP(1);
L1 = Ans;
if(K == 1)
{
printf("%d\n", 2 * (N - 1) - L1 + 1);
return 0;
}
else
{
paint(T1);
paint(T2);
Ans = 0;
memset(Dp, 0, sizeof(Dp));
memset(F, 0, sizeof(F));
memset(Vis, 0, sizeof(Vis));
DP(1);
L2 = Ans;
printf("%d\n", 2 * N - L1 - L2);
}
return 0;
}