(转载请标明作者)
题目:戳这里
//巡逻(APIO2010)
/*
思路: 遍历所有的道路,巡警车需要走的距 离为 14 个单位,每条道路都需要经过两次。
即不加边,巡逻距离为2*(n-1)
那就找树的直径,连两端:
如图a连接2-8,使最长路只走一次
k=1:ans:2*(n-1)-l1+1;
k=2:第二条新路形成的环有2种可能
找到当前树的直径,两端加边
1:与第一个环有重叠(将第一次找到的直径边权赋值为-1)
相当于重叠的边需要走2遍-(-1)=1;
2:没有重叠(1中操作对2没有影响)
ans=2*(n-1)-l1-l2+2;
*/
#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#define il inline
using namespace std;
const int O=1e5+10;
int n,k,head[O],tot,l1,l2,d[O],f[O],s,t,su;
int p,q;
bool vis[O];
struct node{int nxt,v,edge;} a[O*2];
il int read(){
int x=0,w=0;char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return w?-x:x;
}
il void add(int u,int v){
a[++tot].nxt=head[u],a[tot].v=v,head[u]=tot,a[tot].edge=1;
a[++tot].nxt=head[v],a[tot].v=u,head[v]=tot,a[tot].edge=1;
}
void clear(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));}
void cl(){
int t=q;
while(t!=p){
int s=-1;
if(d[t]%2) s=1;
a[d[t]].edge=a[d[t]+s].edge=-1;
t=f[t];
}
}// 改边权
void dfs1(int x,int sum){
vis[x]=1;
if(sum>su) su=sum,p=x;
for(int i=head[x];i;i=a[i].nxt){
int y=a[i].v;
if(vis[y]) continue;
else dfs1(y,sum+a[i].edge);
}
}
void dfs2(int x,int sum,int t){
vis[x]=1;
if(sum>l1) l1=sum,q=x;
for(int i=head[x];i;i=a[i].nxt){
int y=a[i].v;
if(!vis[y]) {
f[y]=x;d[y]=i;//记录路径
dfs2(y,sum+a[i].edge,t);
}
}
}//两遍dfs找直径
void dp(int x){
vis[x]=1;
for(int i=head[x];i;i=a[i].nxt){
int y=a[i].v;
if(!vis[y]){
dp(y);
l2=max(l2,d[x]+d[y]+a[i].edge);
d[x]=max(d[y]+a[i].edge,d[x]);
}
}
}//树形dp找(有负边,成图)
int main(){
n=read(),k=read();
int u,v;
for(int i=1;i<n;i++){
u=read(),v=read();
add(u,v);
}
dfs1(1,0);clear();dfs2(p,0,1);
if(k==2) {cl();clear();dp(1);}
if(k==1) printf("%d",2*n-l1-1);
else printf("%d",2*n-l1-l2);//
return 0;
}