洛谷 P3629 [APIO2010]巡逻(树的直径)

时间:2020-10-16
本文章向大家介绍洛谷 P3629 [APIO2010]巡逻(树的直径),主要包括洛谷 P3629 [APIO2010]巡逻(树的直径)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目链接: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\n",2*(n-1)-(d1-1)-(d2-1));
80     return 0;
81 }
AC代码

 

$flag 上一页 下一页