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

(这道题考察了求直径的两种方法......)

在原图中,每条边要经过两次,增加1条后,形成了一个环,那么环上的边只需要经过一次了(大量画图分析得),再增加一条又会形成一个环,如果这两个环有重叠,重叠部分还是要经过两次,就浪费了,所以我们先找直径(两次dfs),在直径的两个端点连一条边,就可以得到k=1的答案了,如果k=2,将环上的边权都设为-1,再在新图上用DP求新的直径(因为边权有负,要用DP),最后也就得到k=2时的答案了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e5+10;
 4 int to[N<<1],nxt[N<<1],edge[N<<1],head[N],tot;
 5 int n,k,p,q;
 6 int d[N],pre[N],L2;
 7 
 8 void add(int x,int y){
 9     nxt[++tot]=head[x];
10     head[x]=tot;
11     to[tot]=y;
12     edge[tot]=1;
13 }
14 
15 void dfs1(int u,int f){
16     if(d[u]>d[p]) p=u;
17     for(int i=head[u];i;i=nxt[i]){
18         int v=to[i];
19         if(v==f) continue;
20         d[v]=d[u]+edge[i];
22         dfs1(v,u); 
23     }
24 }
25 
26 void dfs2(int u,int f){
27     if(d[u]>d[q]) q=u;
28     for(int i=head[u];i;i=nxt[i]){
29         int v=to[i];
30         if(v==f) continue;
31         d[v]=d[u]+edge[i];
32         pre[v]=i;//记录路径
33         dfs2(v,u); 
34     }
35 }
36 
37 void update(int q,int p){
38     while(q!=p){
39         edge[pre[q]]=-1;
40         edge[pre[q]^1]=-1;//正反向边都变为-1
41         q=to[pre[q]^1]; 
42     }
43 }
44 
45 void dp(int x,int f){//树形DP求直径 
46 //d[]表示向下可以走的最远距离 
47     for(int i=head[x];i;i=nxt[i]){
48         int y=to[i];
49         if(y==f) continue;
50         dp(y,x);
51         L2=max(L2,d[y]+d[x]+edge[i]);
52         d[x]=max(d[x],d[y]+edge[i]);
53     }
54 }
55 
56 int main(){
57     cin>>n>>k;
58     tot=1;
59     for(int i=1;i<n;i++){
60         int x,y;
61         scanf("%d%d",&x,&y);
62         add(x,y);add(y,x);
63     }
64     dfs1(1,0);
65     memset(d,0,sizeof(d));
66     dfs2(p,0);
67     int ans=2*(n-1)-d[q]+1;
68     if(k==1){
69         cout<<ans<<endl;
70         return 0;
71     }
72     update(q,p);
73     memset(d,0,sizeof(d));
74     dp(1,0);
75     cout<<ans-L2+1<<endl;
76 }

 

posted @ 2022-07-18 21:37  YHXo  阅读(48)  评论(0)    收藏  举报