题目
给出一棵树,边权为1。现在加一条或两条边后,使得从1出发遍历每个点至少一次再回到1的路程最短。
分析
先求一次树的直径Max1。然后将直径的边权改为-1,再求一次直径Max2。答案为ans=(n-1)*2-(Max1-1)-(Max2-1)。
code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=400000+10;
struct arr{
int x,y,w;
int next;
}edge[maxn*2];
int edge_m;
int ls[maxn];
void add(int x,int y,int w)
{
edge[++edge_m]=(arr){x,y,w,ls[x]},ls[x]=edge_m;
edge[++edge_m]=(arr){y,x,w,ls[y]},ls[y]=edge_m;
}
int ans;
int root;
int dis[maxn];
int from[maxn];
int u,v;
int dfs(int x,int r)
{
from[x]=x;
for (int i=ls[x];i;i=edge[i].next)
{
if (edge[i].y==r) continue;
dfs(edge[i].y,x);
if (ans<dis[x]+dis[edge[i].y]+edge[i].w)
{
ans=dis[x]+dis[edge[i].y]+edge[i].w;
u=from[x]; v=from[edge[i].y];
}
if (dis[x]<dis[edge[i].y]+edge[i].w)
{
dis[x]=dis[edge[i].y]+edge[i].w;
from[x]=from[edge[i].y];
}
}
}
int flag;
int pre[maxn];
int re(int x,int r,int v)
{
if (x==v)
{
flag=1;
return 0;
}
for (int i=ls[x];i;i=edge[i].next)
{
if (edge[i].y==r) continue;
pre[edge[i].y]=i;
re(edge[i].y,x,v);
if (flag) return 0;
}
}
int n,K;
int main()
{
edge_m=1;
scanf("%d%d",&n,&K);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y,1);
}
ans=0;
dfs(1,0);
if (K==1) printf("%d",2*(n-1)-ans+1);
else {
re(u,0,v);
for (int x=pre[v];x;x=pre[edge[x].x])
{
edge[x].w=-1;
edge[x^1].w=-1;
}
int l=ans;
ans=0;
memset(dis,0,sizeof(dis));
memset(from,0,sizeof(from));
dfs(1,0);
printf("%d",2*n-l-ans);
}
}