10%的数据中,n ≤ 1000, K = 1;
30%的数据中,K = 1;
80%的数据中,每个村庄相邻的村庄数不超过 25;
90%的数据中,每个村庄相邻的村庄数不超过 150;
100%的数据中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。
题解:
先说明一个定义: 树的直径是一棵树上最长的一条路径,从树上任意一个点dfs出离他最远的一个点,再从这个点dfs出离他最远的点,两次dfs出的点之间的路径就是树上最长的链,即最长的一条路径。
k==1 因为要使走的重复的路尽可能的短,所以我们在添加一条边的时候就要保证形成的环中的边尽可能的多,也就是求树上的最长链。ans=2(n-1)-len1+1
k==2 这时候需要在1的基础上再求最长链,因为如果再形成的环与第一个环有重叠的话,那么重叠的部分还是会走两次,所以把第一遍走过的路径付成-1,再求树的直径即可。ans=2(n-1)-len1+1-len2+1
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define N 200003 using namespace std; int n,k; int point[N],next[N],v[N],c[N],tot=-1,ans,ansx; int last[N],len1,len2; void add(int x,int y,int z) { tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z; tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z; } void dfs(int x,int fa,int dis,int num) { if (dis>ans) { ans=dis,ansx=x; } for (int i=point[x];i!=-1;i=next[i]) if (fa!=v[i]) { last[v[i]]=i; dfs(v[i],x,dis+c[i],i); } } void change(int s,int t) { int now=t; while (now!=s) { c[last[now]]=-1; c[last[now]^1]=-1; now=v[last[now]^1]; } } int dp(int x,int fa) { int first=0; int second=0; for (int i=point[x];i!=-1;i=next[i]) if (v[i]!=fa) { int t=c[i]+dp(v[i],x); if (t>first) { second=first; first=t; } else if (t>second) second=t; } if (first+second>ans) ans=first+second; return first; } int main() { scanf("%d%d",&n,&k); tot=-1; memset(point,-1,sizeof(point)); memset(next,-1,sizeof(next)); for (int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y,1); } dfs(1,0,0,0); int head=ansx; ans=0; dfs(ansx,0,0,0); int tail=ansx; len1=ans; change(head,tail); if (k==1) { printf("%d\n",2*(n-1)-ans+1); return 0; } ans=0; dp(1,0); printf("%d\n",2*(n-1)-len1-ans+2); }