1. 程式人生 > >Gym 101964C Tree

Gym 101964C Tree

最長 bre aps 題目 oid -m 答案 大於等於 truct

題目大意:

輸入一棵n個點的樹,每個節點是黑色或白色,現在希望選出m個黑點,使得這m個黑點之間的距離的最大值最小,輸出最小距離。

思路:

我先想到了二分距離,要從[0,n-1]開始(左端點不能從1開始,更不能從m-1開始,這裏我wa了9次)。假設二分出的距離是mid,需要分兩種情況:

  1. mid是偶數,以每一個點為中心,向外找到dis小於等於mid/2的所有點,每個中心統計一次黑點數目,若最大的一次數目大於等於m,就可以縮小距離。
  2. mid是奇數,就是把以每一個點為中心改為每一條邊為中心,中心邊連的兩個點dis都為0,其余操作一樣。

正確性證明:

  • 二分的正確性:假設有一種m個點最大距離最小值為k的答案,同一圖中,現要求m‘個點對應的k‘;當m‘<m時,顯然k可以保證選夠m‘個點,所以一定有k‘<=k; 當m‘>m時,顯然k-1不能保證選夠m‘個點,所以一定有k‘>=k。
  • 檢查時的正確性:就是證明每種最長路為mid的情況不是屬於上述情況1,就是屬於上述情況2。即證明每一個直徑為偶數的樹都以一個點為中心,每一個直徑為奇數的樹都以一條邊的兩個點為中心。(這裏的中心指的是任意一個樹上的點到至少一個中心點的距離都不超過直徑的一半。)

  a.每一個直徑為偶數的樹都以一個點為中心。

  假設設一棵樹T有n個點,直徑為2*m,且任意的點i都存在一個j使得i,j的距離大於m。若有一個點k使得兩個點j1,j2到k的距離都大於m,則j1,j2的距離就大於2*m,矛盾。若每一個點都使得僅有一個點到其距離大於m,則取這  樣的兩對點i,j和k,l,顯然樹上還有聯通這對點的一條路徑,則四條路徑(i,l),(i,k),(j,k),(j,l)中一定有一條要經過邊(i,j)和(k,l),這樣它們的距離就大於2*m了,矛盾。

所以存在一個中心點。

  b.每一個直徑為奇數的樹都以一條邊的兩個點為中心。

  設一棵樹T有n個點,直徑為2*m+1,找到一條直徑(x1,x2,...,xm+1,xm+2,...x2*m+2),將最中間的xm+1,xm+2合成一個點y,刪去邊(xm+1,xm+2),將與xm+1,xm+2連的邊連向y,即證明所有點到y的距離都不大於m。假設存在點k使dis(k,y)大於m,不妨設k原來與xm+2連,則k到x1的距離為dis(x1,xm+2)+dis(k,y),大於2*m+1,矛盾。

所以樹T以xm+1,xm+2為中心。

技術分享圖片
  1 #include<cstdio>
  2
#include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 6 using namespace std; 7 8 int n,m,bianshu=0,cor[200],head[200]; 9 int que[2000],st,ed,cnt,used[200],dis[200],yy[200][3]; 10 struct node 11 { 12 int zhong,next; 13 node() 14 { 15 zhong=0; 16 next=0; 17 } 18 }bian[200*5]; 19 20 void comq(int x) 21 { 22 st++; 23 used[x]=1; 24 que[st]=x; 25 return ; 26 } 27 28 void jiabian(int qi,int zh) 29 { 30 bianshu++; 31 bian[bianshu].next=head[qi]; 32 bian[bianshu].zhong=zh; 33 head[qi]=bianshu; 34 return ; 35 } 36 37 bool check1(int x) 38 { 39 //printf("X=%d\n",x); 40 int maxx=0; 41 for(int i=1;i<=n;i++) 42 { 43 memset(used,0,sizeof(used)); 44 memset(que,0,sizeof(que)); 45 memset(dis,0,sizeof(dis)); 46 st=0; 47 ed=0; 48 cnt=0; 49 comq(i); 50 if(cor[i]==1)cnt++; 51 dis[i]=0; 52 while(st!=ed) 53 { 54 ed++; 55 int dang=que[ed]; 56 if(dis[dang]>=(x/2))break; 57 int k=head[dang]; 58 while(k) 59 { 60 if(!used[bian[k].zhong]) 61 { 62 if(cor[bian[k].zhong]==1)cnt++; 63 comq(bian[k].zhong); 64 dis[bian[k].zhong]=dis[dang]+1; 65 } 66 k=bian[k].next; 67 } 68 } 69 maxx=max(cnt,maxx); 70 } 71 if(maxx>=m)return true; 72 else return false; 73 } 74 75 bool check2(int x) 76 { 77 x--; 78 int maxx=0; 79 for(int i=1;i<n;i++) 80 { 81 memset(used,0,sizeof(used)); 82 memset(que,0,sizeof(que)); 83 memset(dis,0,sizeof(dis)); 84 st=0; 85 ed=0; 86 cnt=0; 87 comq(yy[i][1]); 88 if(cor[yy[i][1]]==1)cnt++; 89 dis[yy[i][1]]=0; 90 comq(yy[i][2]); 91 if(cor[yy[i][2]]==1)cnt++; 92 dis[yy[i][2]]=0; 93 while(st!=ed) 94 { 95 ed++; 96 int dang=que[ed]; 97 if(dis[dang]>=(x/2))break; 98 int k=head[dang]; 99 while(k) 100 { 101 if(!used[bian[k].zhong]) 102 { 103 if(cor[bian[k].zhong]==1)cnt++; 104 used[bian[k].zhong]=1; 105 st++; 106 que[st]=bian[k].zhong; 107 dis[bian[k].zhong]=dis[dang]+1; 108 } 109 k=bian[k].next; 110 } 111 } 112 maxx=max(cnt,maxx); 113 } 114 if(maxx>=m)return true; 115 else return false; 116 } 117 118 bool ok(int x) 119 { 120 if(x&1)return check2(x); 121 else return check1(x); 122 } 123 124 int main() 125 { 126 scanf("%d%d",&n,&m); 127 for(int i=1;i<=n;i++)scanf("%d",&cor[i]); 128 for(int i=1;i<n;i++) 129 { 130 int a,b; 131 scanf("%d%d",&a,&b); 132 yy[i][1]=a; 133 yy[i][2]=b; 134 jiabian(a,b); 135 jiabian(b,a); 136 } 137 if((m==1)||(n==1)) 138 { 139 printf("0\n"); 140 return 0; 141 } 142 int l=0; 143 int r=n; 144 while(l<r) 145 { 146 int mid=(l+r)>>1; 147 if(ok(mid))r=mid; 148 else l=mid+1; 149 } 150 printf("%d\n",r); 151 return 0; 152 }
View Code

Gym 101964C Tree