[XSY 1148][二分]隔热板

我们先二分一下,问题转化成在一个圆上,有若干条弧,问最少在圆上安排多少个点,使得每段弧中至少有一个点。我们先排序去重。然后,通过简单的倍增得出。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef double db;
const db eps=1e-5,pi2=6.2831853071795864769252867665;
int n,m;
#define Maxn 40010
struct P{
	int x,y;
	db a,d;
	void calc(){
		a=atan2(y,x);
		if(a<0)a+=pi2;
		d=sqrt(x*x+y*y);
	}
}p[Maxn];
struct Seg{
	db l,r;
	int id;
	bool operator <(const Seg &z)const{return r==z.r?l>z.l:r<z.r;}
	bool operator ==(const Seg &z)const{return l==z.l&&r==z.r;}
}seg[Maxn<<1];
int f[Maxn<<1][17];
bool cover[Maxn];
int cnt=0;
inline bool Judge(db r){
	memset(cover,false,sizeof(cover));
	for(int i=1;i<=n;++i){
		db d=acos(r/p[i].d);
		seg[i]=(Seg){p[i].a-d,p[i].a+d,i};
		seg[i+n]=(Seg){p[i].a-d+pi2,p[i].a+d+pi2,i};
	}
	sort(seg+1,seg+2*n+1);
	for(int i=1;i<2*n;++i)
		if(seg[i+1].l<=seg[i].l){
			cover[seg[i+1].id]=true;
			seg[i+1]=seg[i];
		}
	cnt=0;
	for(int i=1;i<=n;++i)
		if(!cover[i]){
		    db d=acos(r/p[i].d);
		    seg[++cnt]=(Seg){p[i].a-d,p[i].a+d,i};
		    seg[++cnt]=(Seg){p[i].a-d+pi2,p[i].a+d+pi2,i};
		}
	sort(seg+1,seg+cnt+1);
	for(int i=1,nex=1;i<=cnt;++i){
		while(nex<=cnt&&seg[nex].l<=seg[i].r)nex++;
		f[i][0]=nex;
	}
	f[cnt+1][0]=cnt+1;
	for(int j=1;j<=16;++j)
		for(int i=1;i<=cnt+1;++i)f[i][j]=f[f[i][j-1]][j-1];
	int ans=(cnt>>1);
    for(int i=1;i<=(cnt>>1);++i){
    	int res=0,u=i;
    	for(int j=16;j>=0;--j)
    		if(f[u][j]<i+(cnt>>1)){
    			u=f[u][j];
    			res|=(1<<j);
    		}
    	ans=min(ans,res+1);
    }
    return ans<=m;
}
int main(){
	db l=0,r=1000000000.0;
	scanf("%d%d",&n,&m);
	for(register int  i=1;i<=n;++i){
		scanf("%d%d",&p[i].x,&p[i].y);
		p[i].calc();
		r=min(r,p[i].d);
	}
	while(r-l>eps){
		db mid=(l+r)*0.5;
		if(Judge(mid))l=mid;
		else r=mid;
	}
	printf("%.2lf\n",r);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ezoilearner/article/details/83067518