[二分答案 随机增量法] BZOJ 2280 [Poi2011]Plot

26 篇文章 0 订阅
22 篇文章 1 订阅

狂T 只能去%Po姐 http://blog.csdn.net/popoqqq/article/details/45100989


直接上二分是不可取的,因为我们要求m次,如果每次都验证一遍[1,n/2]直接就炸了

我们可以这么搞

首先判断[pos,pos+1-1]是否满足要求

然后判断[pos,pos+2-1]是否满足要求

然后判断[pos,pos+4-1]是否满足要求

然后判断[pos,pos+8-1]是否满足要求

...

直到找到一个不满足要求2^k的为止,然后我们在[2^(k-1),2^k]区间内二分

这样可以保证复杂度为O(nlognlog(limit/eps)) 不过常数巨大。。。。


虽然还是不知道怎么保证复杂度的

被卡精度 还是不得已动用了不愿用的long double

另外 白天不要交此题 否则RP--


#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
using namespace std;
typedef long double ld;

const ld eps=1e-9;

inline int dcmp(ld a,ld b)  
{  
    if (fabs(a-b)<eps) return 0;  
    if (a<b) return -1; return 1;  
}

inline ld sqr(ld x){
	return x*x;
}

const int N=100005;

struct Point{
	ld x,y;
	Point(ld x=0,ld y=0):x(x),y(y) { }
	void read(){
		double ix,iy;
		scanf("%lf%lf",&ix,&iy); x=ix; y=iy;
	}
	friend ld Dist(Point A,Point B){
		return sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));
	}
}P[N],p[N];

struct abcd{
	Point c; ld r;
	abcd() { }
	abcd(Point _c,ld _r){
		c=_c; r=_r;
	}
}Q[N];
int K;

inline Point Get(Point a,Point b,Point c)  
{  
	ld a1,a2,b1,b2,c1,c2;  
	Point ans;  
	a1=2*(b.x-a.x),b1=2*(b.y-a.y),c1=sqr(b.x)-sqr(a.x)+sqr(b.y)-sqr(a.y);  
	a2=2*(c.x-a.x),b2=2*(c.y-a.y),c2=sqr(c.x)-sqr(a.x)+sqr(c.y)-sqr(a.y);  
	if(!dcmp(a1,0))
		ans.y=c1/b1,ans.x=(c2-ans.y*b2)/a2;
	else if(!dcmp(b1,0))
		ans.x=c1/a1,ans.y=(c2-ans.x*a2)/b2;
	else 
	    ans.x=(c2*b1-c1*b2)/(a2*b1-a1*b2),
		ans.y=(c2*a1-c1*a2)/(b2*a1-b1*a2);
	return ans;   
}  
  
inline Point Get(Point A,Point B)  
{
    return Point((A.x+B.x)/2,(A.y+B.y)/2);  
}
  
inline abcd MinCover(int l,int r)  
{  
	int n=r-l+1;
	for (int i=l;i<=r;i++) p[i-l+1]=P[i];
	random_shuffle(p+1,p+n+1);
    Point _C=p[1]; ld _R=0;  
    for (int i=2;i<=n;i++)  
	  	if (dcmp(Dist(p[i],_C),_R)>0) 
	  	{
	  		_C=p[i],_R=0;  
            for (int j=1;j<=i;j++)  
                if (dcmp(Dist(p[j],_C),_R)>0)  
                {
                    _C=Get(p[i],p[j]),_R=Dist(_C,p[i]);  
                    for (int k=1;k<=j;k++)  
                        if (dcmp(Dist(p[k],_C),_R)>0)
                            _C=Get(p[i],p[j],p[k]),_R=Dist(_C,p[i]);
                }  
        }
    return abcd(_C,_R);
}

int n,m;
ld L,R,MID;

inline int calc(int s,int k){
	int i;  
    for(i=1;;i=min(i<<1,n-s+1))  
    {  
        if(dcmp(MinCover(s,s+i-1).r,MID)>0)  
            break;  
        if(i==n-s+1) return n;  
    }
    int l=s+(i>>1)-1,r=s+i-1,mid; abcd ret; 
	while (l+1<r)
	{
		if (dcmp(MinCover(s,mid=(l+r)>>1).r,MID)>0)
			r=mid;
		else
			l=mid;
	}
	return l;
}

inline bool check()
{
	K=0;
	for (int s=1,t;s<=n && K<=m;s=t+1)
	{
		K++;
		t=calc(s,K);
	}
	return K<=m;
}

inline void Bin(){
	L=0,R=MinCover(1,n).r;
	while (R-L>1e-10)
	{
		MID=(L+R)/2;
		if (check())
			R=MID;
		else
			L=MID;
	}
	MID=R;
	K=0;
	for (int s=1,t;s<=n;s=t+1)
	{
		K++;
		t=calc(s,K); Q[K]=MinCover(s,t);
	}
	printf("%.15lf\n%d\n",(double)R,K);
	for (int i=1;i<=K;i++)
		printf("%.15lf %.15lf\n",(double)Q[i].c.x,(double)Q[i].c.y);
}

int main()
{
	srand(10086);
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) P[i].read();
	Bin();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值