1. 程式人生 > >【APIO2010】巡邏

【APIO2010】巡邏

show push 樹的直徑 esp sizeof -a closed ret 一道

這是一道關於樹的直徑的好題,值得一刷。

本題有兩個難點,一個是分類討論k,另一個是代碼的實現(其實還好)。

本題k可以為1或2,因此我們分類討論一下。

  • 當k=1時,我們可以任選兩個點連接,假設我們一條邊都不連接,那麽我們需要走2*m次,其中m為邊的數量。假設我們在x,y上連一條邊,那麽我們用1個距離節省了dis(x,y)個距離,為了使答案最小化,我們要使dis(x,y)最大,顯然我們求一遍樹的直徑即可,那麽答案為2*m-zhijing+1.
  • 當k=2時,就是在k=1的基礎上再加上一條邊,同樣我們分成兩部分討論。假設這一個環與上一個環沒有重合的邊,那麽問題很簡單,只需要在上一個答案的基礎上再減去dis(x,y)再+1即可。若這個環與上一個環有重合的部分,為了保證每一條邊都被巡邏,那麽我們發現重合的這部分需要走兩次,環上剩下的部分走一次即可。這是我們把第一次求的直徑上的邊權都賦值為-1,然後再做一遍樹的直徑得到的答案就是第二次的解,所以這種情況的答案就是2*m-(zhijing1-1)-(zhijing2-1)=2*m-zhijing1-zhijing2+2.如果對於兩個環沒有重合的情況,這個答案依然成立。

我們先進行兩次bfs求出樹的直徑,在bfs的同時記錄在直徑上與每一個點的相連的邊,方便之後賦值-1,之後再進行一次樹形dp求出樹的直徑即可。

技術分享圖片
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <queue>
 5 using namespace std;
 6 inline int read() {
 7     int ret=0;
 8     int op=1;
 9     char c=getchar();
10 while(c<0||c>9) {if(c==-) op=-1; c=getchar();} 11 while(c<=9&&c>=0) ret=ret*10+c-0,c=getchar(); 12 return ret*op; 13 } 14 struct node { 15 int next,to,dis; 16 }a[100010<<1]; 17 int n,head[100010<<1],num=1,k,dis[100010],pre[100010],ret,x,maxx; 18 queue<int
> q; 19 inline void add(int from,int to,int dis=1) { 20 a[++num].next=head[from]; 21 a[num].to=to; 22 a[num].dis=dis; 23 head[from]=num; 24 } 25 int bfs(int s) { 26 while(!q.empty()) q.pop(); 27 memset(dis,0x3f,sizeof(dis)); 28 dis[s]=pre[s]=0; 29 q.push(s); 30 while(!q.empty()) { 31 int now=q.front(); 32 q.pop(); 33 for(int i=head[now];i;i=a[i].next) { 34 int v=a[i].to; 35 if(dis[v]==0x3f3f3f3f) { 36 dis[v]=dis[now]+a[i].dis; 37 pre[v]=i; 38 q.push(v); 39 } 40 } 41 } 42 int y=1; 43 for(int i=1;i<=n;i++) 44 if(dis[i]>dis[y]) y=i; 45 return y; 46 } 47 int zhijing() { 48 ret=bfs(1); 49 ret=bfs(ret); 50 return dis[ret]; 51 } 52 int dp(int u,int fa) { 53 int sum1=0,sum2=0; 54 for(int i=head[u];i;i=a[i].next) { 55 int v=a[i].to; 56 if(v==fa) continue ; 57 sum2=max(sum2,dp(v,u)+a[i].dis); 58 if(sum2>sum1) swap(sum2,sum1); 59 } 60 maxx=max(maxx,sum1+sum2); 61 return sum1; 62 } 63 int main() { 64 n=read(); k=read(); 65 for(int i=1;i<n;i++) { 66 int x=read(),y=read(); 67 add(x,y); 68 add(y,x); 69 } 70 x=zhijing(); 71 if(k==1) { 72 printf("%d\n",2*(n-1)-x+1); 73 return 0; 74 } 75 while(pre[ret]) { 76 a[pre[ret]].dis=a[pre[ret]^1].dis=-1; 77 ret=a[pre[ret]^1].to; 78 } 79 dp(1,0); 80 printf("%d\n",2*n-x-maxx); 81 return 0; 82 }
AC Code

【APIO2010】巡邏