BZOJ2429 [HAOI2006]聪明的猴子
题目描述:
在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的
地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出
水面的不同树冠上来回穿梭,以找到喜欢吃的果实。现在,在这个地区露出水面的有N棵树,假设每棵树本身的
直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任
意两棵树的坐标都不相同)。在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被
大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然
也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判
断出自己能否跳到对面的树上。【问题】 现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的
每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食
题解:
最小生成树裸题。
既然一定要跳遍所有的树,我们可以求出原图的最小生成树。
猴子如果能自由地觅食,那么就一定满足这样一个条件:
即猴子的跳跃距离一定大于等于原图最小生成树的最大边
附上Kruskal代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int m,a[501],n,xs[1001],ys[1001],idx,f[1001],k,anss; double ans; struct Edge { int x,y; double v; }dis[1000001]; double get_dis(int x1,int y1,int x2,int y2) { return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } bool cmp(const Edge &l,const Edge &r) { return l.v<r.v; } int find(int p) { if(f[p]!=p) f[p]=find(f[p]); return f[p]; } void merge(int l,int r) { int fx=find(l); int fy=find(r); if(fx!=fy) f[fx]=fy; } int main() { scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&a[i]); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&xs[i],&ys[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { dis[++idx].x=i; dis[idx].y=j; dis[idx].v=get_dis(xs[i],ys[i],xs[j],ys[j]); } for(int i=1;i<=n;i++) f[i]=i; sort(dis+1,dis+n*n+1,cmp); for(int i=1;i<=n*n;i++) { if(find(dis[i].x)!=find(dis[i].y)) { merge(dis[i].x,dis[i].y); ans=max(ans,dis[i].v); k++; } if(k==n-1) break; } for(int i=1;i<=m;i++) if((double)a[i]>=ans) anss++; printf("%d",anss); }