题目链接:https://www.luogu.com.cn/problem/P3629
首先如果不添加任何道路,每条边会经过两次,那么所经过的路径长度应该是2*(n-1)。
分析可得,当添加一条道路时,会形成一个环,这个环上且属于原来树上的点只会经过一次。所以可以将直径的两个端点连起来,会使减小的路径最大。
因此可以得到k=1的思路:两次DFS求出树的直径d,输出2*(n-1)-(d-1)。
当添加两条道路时,又会形成一个环,如果两个环不相交,那么答案会继续减小,但是环可能会重合,而对于重合的部分,还是要经过两次。
所以可以得到k=2的思路:两次DFS求出原数的直径d1,并记录路径。将直径上的所有边权改为-1,表示如果这样走就与第一个环重复,并且在最后答案统计的时候相当于把重合的部分加了回来。用树形DP求树的直径长度d2。
(注意第一次只能用DFS,因为要记录路径,而第二次只能用树形DP,因为有负边权,距离点u远的点不一定真的远)。
最终的答案即为2*(n-1)-(d1-1)-(d2-1)。
注意边权要赋在边上,虽然比较难写:主要在求出d1的路径后,将这个路径上的所有边权改为-1。注意边是双向边,要将两个方向的边权都改成-1。
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #include<cstring> 5 using namespace std; 6 const int N=100005; 7 const int INF=2147483647; 8 int n,k,maxd,tot,head[N],f[N],dis[N],vis[N],p,w[N],ans; 9 struct node{ 10 int to,next,w; 11 }edge[N<<1]; 12 void add(int u,int v,int w){ 13 edge[tot].to=v; 14 edge[tot].next=head[u]; 15 edge[tot].w=w; 16 head[u]=tot++; 17 } 18 void DFS(int u,int fa){ 19 f[u]=fa; 20 if(maxd<dis[u]){ 21 maxd=dis[u]; 22 p=u; 23 } 24 for(int i=head[u];i!=-1;i=edge[i].next){ 25 int v=edge[i].to; 26 if(vis[v]||v==fa) continue; 27 vis[v]=1; 28 dis[v]=dis[u]+edge[i].w; 29 DFS(v,u); 30 } 31 } 32 void DP(int u){ 33 vis[u]=1; 34 for(int i=head[u];i!=-1;i=edge[i].next){ 35 int v=edge[i].to; 36 if(vis[v]) continue; 37 DP(v); 38 ans=max(ans,dis[u]+dis[v]+edge[i].w); 39 dis[u]=max(dis[u],dis[v]+edge[i].w); 40 } 41 } 42 int main(){ 43 memset(head,-1,sizeof(head)); 44 memset(f,-1,sizeof(f)); 45 scanf("%d%d",&n,&k); 46 for(int i=1;i<=n-1;i++){ 47 int a,b; 48 scanf("%d%d",&a,&b); 49 add(a,b,1); add(b,a,1); 50 } 51 int a,b,d1,d2; 52 DFS(1,-1); 53 a=p; 54 maxd=0; 55 memset(dis,0,sizeof(dis)); 56 memset(f,-1,sizeof(f)); 57 memset(vis,0,sizeof(vis)); 58 DFS(p,-1); 59 b=p; 60 d1=maxd; 61 if(k==1){ 62 printf("%d",2*(n-1)-(d1-1)); 63 return 0; 64 } 65 for(int i=b;i!=-1;i=f[i]){ 66 for(int j=head[i];j!=-1;j=edge[j].next){ 67 int v=edge[j].to; 68 if(v==f[i]) edge[j].w=-1; 69 } 70 for(int j=head[f[i]];j!=-1;j=edge[j].next){ 71 int v=edge[j].to; 72 if(v==i) edge[j].w=-1; 73 } 74 } 75 memset(vis,0,sizeof(vis)); 76 memset(dis,0,sizeof(dis)); 77 DP(1); 78 d2=ans; 79 printf("%d ",2*(n-1)-(d1-1)-(d2-1)); 80 return 0; 81 }