Gym 101964C Tree

题目大意:

    输入一棵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

 

posted @ 2018-11-18 17:08  liqgnonqfu  阅读(306)  评论(0编辑  收藏  举报