1. 程式人生 > 實用技巧 >洛谷 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程式碼