Gym 101964C Tree
Posted liqgnonqfu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gym 101964C Tree相关的知识,希望对你有一定的参考价值。
题目大意:
输入一棵n个点的树,每个节点是黑色或白色,现在希望选出m个黑点,使得这m个黑点之间的距离的最大值最小,输出最小距离。
思路:
我先想到了二分距离,要从[0,n-1]开始(左端点不能从1开始,更不能从m-1开始,这里我wa了9次)。假设二分出的距离是mid,需要分两种情况:
- mid是偶数,以每一个点为中心,向外找到dis小于等于mid/2的所有点,每个中心统计一次黑点数目,若最大的一次数目大于等于m,就可以缩小距离。
- 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 ",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 "); 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 ",r); 151 return 0; 152 }
以上是关于Gym 101964C Tree的主要内容,如果未能解决你的问题,请参考以下文章
CF gym102759IQuery On A Tree 17
Gym101158G Placing Medals on a Binary Tree(二进制模拟)