BZOJ2280: [Poi2011]Plot

来源:互联网 发布:蒙古输入法软件下载 编辑:程序博客网 时间:2024/04/29 17:00

题目大意:给定n个点和m,要求把n个点分成m段,使得每段的最小覆盖圆的半径的最大值最小


肯定能想到要二分答案,然后看看怎么判定

对于每个点来说,需要找到序列中最远能到达的从他开始符合条件的点

但是因为最小圆覆盖需要随机打乱点才能保证时间复杂度,所以我们不能依次推出最小覆盖圆,那么能想到二分

但是每次检验是O(长度)的,而我们需要二分m次,直接二分可能会使时间复杂度变成O(logINF*NMlogN)

所以我们需要用到一些小技巧,就是先检验长度为1的方案,然后检验长度为2的方案,然后4,8.....直到找到一个不合法的k

然后我们紧接着在[k/2,k]这个区间里二分就可以了

因为这样这次搜索的二分上限为O(KlogN),而对答案的贡献至少为K,所以当对答案的贡献为N(即扫到最后)时,时间复杂度上限为O(NlogN)

所以总时间复杂度变成了O(NlogNlogINF)


PS:这题在POI上单点220s!!!!!!!BZOJ总时限300s差评!!害的我卡了好几发OJ,最后调了调eps,300.198s卡时过了....


#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 100010#define eps 2e-8using namespace std;struct node{long double x,y;};long double sqr(long double x){return x*x;}long double dis(node x,node y){return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));}node a[N],b[N];int n,m;node O;long double R;node jfc(long double a,long double b,long double c,long double d,long double e,long double f){long double y=(a*f-c*d)/(b*d-a*e);long double x=(b*f-c*e)/(a*e-b*d);return (node){x,y};}bool incircle(node x){if(dis(x,O)<=R+eps) return true;return false;}void solve(int l,int r){int i,j,k;for(i=l;i<=r;i++)b[i-l+1]=a[i];int nn=r-l+1;random_shuffle(b+1,b+nn+1);R=0;for(i=1;i<=nn;i++)if(!incircle(b[i])){O=b[i];R=0;for(j=1;j<i;j++)if(!incircle(b[j])){O.x=(b[i].x+b[j].x)/2;O.y=(b[i].y+b[j].y)/2;R=dis(O,b[i]);for(k=1;k<j;k++)if(!incircle(b[k])){O=jfc(b[i].x-b[j].x,b[i].y-b[j].y,(sqr(b[j].x)+sqr(b[j].y)-sqr(b[i].x)-sqr(b[i].y))/2,b[i].x-b[k].x,b[i].y-b[k].y,(sqr(b[k].x)+sqr(b[k].y)-sqr(b[i].x)-sqr(b[i].y))/2);R=dis(O,b[i]);}}}}int far(int st,long double limit){int i,j;for(i=1;;i++){solve(st,min(st+(1<<i)-1,n));if(R>limit+eps) break;if(st+(1<<i)-1>=n) return n;}int l=st+(1<<(i-1)),r=min(st+(1<<i)-1,n),mid;while(l<r){mid=(l+r)>>1;solve(st,mid);if(R>limit+eps) r=mid;else l=mid+1;}return l-1;}bool judge(long double limit){int i,j,tot=0;for(i=1;i<=n;i=j+1){if(tot==m) return false;j=far(i,limit);tot++;}return true;}int main(){/*freopen("wyk.in","r",stdin);freopen("wyk.out","w",stdout);*/scanf("%d%d",&n,&m);int i,j;double tmp1,tmp2;for(i=1;i<=n;i++){scanf("%lf%lf",&tmp1,&tmp2);a[i].x=tmp1;a[i].y=tmp2;}solve(1,n);long double l=0,r=R,mid;while(r-l>eps){mid=(l+r)/2;if(judge(mid)) r=mid;else l=mid;}printf("%.15lf\n%d\n",(double)l,m);j=1;for(i=1;i<=m;i++){if(j>n){puts("0 0");continue;}int tmp=far(j,l+eps);solve(j,tmp);printf("%.15lf %.15lf\n",(double)O.x,(double)O.y);j=tmp+1;}}


0 0
原创粉丝点击