Input
第一行包含两个整数 n, K(1 ≤ K ≤ 2)。接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n)。
Output
输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离。
Sample Input
8 1
1 2
3 1
3 4
5 3
7 5
8 5
5 6
Sample Output
11
HINT
10%的数据中,n ≤ 1000, K = 1;
30%的数据中,K = 1;
80%的数据中,每个村庄相邻的村庄数不超过 25;
90%的数据中,每个村庄相邻的村庄数不超过 150;
100%的数据中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。
最初线路总长度为2 * (n-1),当K=1时,找到树的最长链,设长度为L1,答案为2 * (n-1)-L1+1;当K=2时,将直径边权取反,再次求直径,设长度为L2,答案为2*n-L1-L2。
#include<cstdio>
const int N=1e5+10;
struct Edge{
int v,w,nx;
}edge[N<<1];
int head[N],tot=1,n,k,pre1[N],pre2[N],len,rt;
inline void addedge(int u,int v)
{
edge[++tot].v=v;
edge[tot].w=1;
edge[tot].nx=head[u];
head[u]=tot;
}
int dfs(int u,int fa)
{
int mx1=0,mx2=0;//最长路与次长路
for(int i=head[u];i;i=edge[i].nx)
{
int v=edge[i].v;
if(v!=fa)
{
int w=edge[i].w+dfs(v,u);
if(w>mx1)mx2=mx1,mx1=w,pre2[u]=pre1[u],pre1[u]=i;
else if(w>mx2)mx2=w,pre2[u]=i;
}
}
if(mx1+mx2>len)len=mx1+mx2,rt=u;
return mx1;
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&k);
int u,v,ans=2*(n-1);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(1,0);ans=ans-len+1;
if(k==2)
{
len=0;
for(int i=pre1[rt];i;i=pre1[edge[i].v])edge[i].w=edge[i^1].w=-1;//成对变换修改正反边
for(int i=pre2[rt];i;i=pre1[edge[i].v])edge[i].w=edge[i^1].w=-1;
dfs(1,0);
ans=ans-len+1;
}
printf("%d\n",ans);
return 0;
}
总结
通过边权取反的方法处理两个环重叠的情况,非常巧妙。