CodeForces 549E Sasha Circle 弱鸡的题解报告

    题意:给定两堆点,问是否存在一个圆分开这两堆点。(使得一堆点在圆内或圆上,一堆点在圆外)

首先附英文官方题解: http://codeforces.com/blog/entry/18363 (弱鸡的我表示看不明白)

网上仅存的一篇题解,看得我是头皮发麻,表示看不懂- -;唉,还是太弱了。

没办法,只能自己硬啃AC代码。


现在整理一下思路:

    首先我们要确定,如果存在一个圆将一堆点A包含在内,而另一堆点B在外的话,该圆的半径一定是大于或者等于这堆点A所确定的最小半径(可以理解为最小圆覆盖问题的那个半径),而且小于或者等于这堆点A所确定的最大半径的圆(最暴力的获得最大半径的做法:暴力枚举三个点,然后求半径【三点确定一个圆】,最大的那个值即最大半径,但是题目的数据范围显然不允许我们暴力解决问题)。

    然后我们再确定一点,对于一堆在圆内的点,只有凸包上的点是有用的,凸包内的点此时可以忽略。所以这个问题我们需要求凸包,O(nlogn)级别的时间复杂度可以接受。

    剩下的问题就是怎么高效地去寻找一个对应半径的圆,使得满足题意(又或者满足题意的圆不存在)。最最最暴力的方法:枚举三个点,求出这三个点确定的圆的半径,然后逐个逐个点判断是否满足题意,毫无疑问,时间复杂度绝对爆炸,但是最起码这是解决问题的一种思路,接下来我们就只需要利用一些操作去优化这个寻找的过程。

首先是高效地解决寻找圆的半径的问题:解决这类问题有一个思路,仔细观察,你会发现,每一个大问题,可以分解为两个较小的问题,(类似于二分),比如说:要求一堆点的最近点对,我们可以将这堆点从中间分成左右两部分,这样问题可以分解为求左半部分的最近点对和右半部分的最近点对,最后再比较合并(中间需要特殊处理,这里不详谈),然后最于每个子问题,也可以看成父问题一样去处理,这样一来本来需要O(n^2)时间复杂度的问题,就可以在O(nlogn)时间内解决(因为二分是O(logn)级别的),这种思想区别于二分,但本质差不多,叫做分治。

回到我们的问题,我们需要找到所有的半径,我们可以从大到小枚举。我们利用分治的思想,首先我们固定凸包上的两个端点,去枚举一个点,这样可以在O(n)时间复杂度求到一个最大半径,对于获得的最大半径,我们从最大半径处切开,分成两半,分别对这两半进行同样的操作,这样一来,我们就可以获得所有细分区域的最大半径(对于一堆点,要找一个最小覆盖圆,找的就是这个最大半径),然后我们对于每一个细分区域再进行对于圆外的点的比较操作。时间复杂度约等于O(nlogn),可以接受。

接下来就是还剩比较操作了,这一部分非常恶心,细节很多,稍微不注意就gg。详情看代码。


最后附AC代码:

//#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstdio>
#include<queue>
#include<ctime>
using namespace std;

#pragma comment(linker, "/STACK:102400000,102400000")

#define up(i,j,k) for(int i=j;i<k;i++)
#define uup(i,j,k) for(int i=j;i>=k;i--)
#define mem(i,j) memset(i,j,sizeof i)
#define sfi(i) scanf("%d",&i)
#define sfl(i) scanf("%lld",&i)
#define sfd(i) scanf("%lf",&i)
#define sfc(i) scanf("%c",&i)
#define sfs(i) scanf("%s",i)
#define sf() cout<<"This is a flag!"<<endl;
#define wt(i) cout<<"This is "<<i<<endl;
#define ll long long
#define mod(x) (x)%mod
#define fre() freopen("d:\\1.txt","r",stdin)
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x7f7f7f7f;
const int mod = 1e9 + 7;
const int MAX = 1e4 + 50;
const double dinf=0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f;
int n,m;
struct node
{
    double x,y;
}p[2][MAX],temp[MAX];

vector<node>pp[2];

double sqr(double xx)
{
    return xx*xx;
}
double Get_Dis(const node &a,const node &b)
{
    return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
double Cross(const node &p1,const node &p2,const node &p3)
{
    double x1=p2.x-p1.x;
    double y1=p2.y-p1.y;
    double x2=p3.x-p1.x;
    double y2=p3.y-p1.y;
    return x1*y2-x2*y1;
}
int epss(const double xx)
{
    if(fabs(xx)<eps)
        return 0;
    return xx>0?1:-1;
}
node mid;
bool cmp(const node &a,const node &b)
{
    double C=Cross(mid,a,b);
    if(epss(C)==0)
        return Get_Dis(a,mid)<Get_Dis(b,mid);
    if(epss(C)>0)
        return true;
    return false;
}
void Graham(int xx)
{
    //pp[xx].clear();
    mid=p[xx][0];
    int nn;
    if(xx==0)
        nn=n;
    else
        nn=m;
    if(nn<=2)
    {
        //sf();
        up(i,0,nn)
        {
            pp[xx].push_back(p[xx][i]);
        }
        return;
    }
    int x=0;
    up(i,1,nn)
    {
        if(epss(p[xx][i].y-mid.y)<0||epss(p[xx][i].y-mid.y)==0&&epss(p[xx][i].x-mid.x)<0)
        {
            mid=p[xx][i];
            x=i;
        }
    }
    swap(p[xx][x],p[xx][0]);
    sort(p[xx]+1,p[xx]+nn,cmp);
    temp[0]=p[xx][0];
    temp[1]=p[xx][1];
    //sf();
    //cout<<p[xx][1].x<<' '<<p[xx][1].y<<endl;
    int cnt=1;
    up(i,2,nn)
    {
        //sf();
        while(cnt&&epss(Cross(temp[cnt-1],temp[cnt],p[xx][i])<=0))
            cnt--;
        cnt++;
        temp[cnt]=p[xx][i];
    }
    //cout<<cnt<<endl;
    up(i,0,cnt+1)
    {
        pp[xx].push_back(temp[i]);
       // cout<<temp[i].y<<endl;
    }
    return;
}

node Get_Center(const node &p1,const node &p2,const node &p3)
{
    double a=p1.x-p2.x;
    double b=p1.y-p2.y;
    double c=p1.x-p3.x;
    double d=p1.y-p3.y;
    double e=((p1.x*p1.x-p2.x*p2.x)+(p1.y*p1.y-p2.y*p2.y))/2.0;
    double f=((p1.x*p1.x-p3.x*p3.x)+(p1.y*p1.y-p3.y*p3.y))/2.0;
    double det=b*c-a*d;
    node Center;
    Center.x=-(d*e-b*f)/det;
    Center.y=-(a*f-c*e)/det;
    return Center;
}

bool Judge_mid(const node &p1,const node &p2,const node &p3)
{
    if(epss(p1.x-p2.x)==0)
    {
        if(epss(p1.y-p2.y)>0)
        {
            if(epss(p2.y-p3.y)>0)
            {
                return true;
            }
            return false;
        }
        else
        {
            if(epss(p2.y-p3.y)<0)
            {
                return true;
            }
            return false;
        }
    }
    else
    {
        if(epss(p1.x-p2.x)>0)
        {
            if(epss(p2.x-p3.x)>0)
            {
                return true;
            }
            return false;
        }
        else
        {
            if(epss(p2.x-p3.x)<0)
            {
                return true;
            }
            return false;
        }
    }
}
bool Judge_Inside(int x1,int x2,int l,int r)
{
    double LMAX=dinf,RMAX=-dinf;
    int xx=-1;
    bool flag=1,flagg=1;
    up(i,l+1,r)
    {
        if(epss(Cross(pp[x1][l],pp[x1][r],pp[x1][i]))==0)
        {
            flag=0;
            break;
        }
        node Center=Get_Center(pp[x1][l],pp[x1][r],pp[x1][i]);
        mid.x=(pp[x1][l].x+pp[x1][r].x)/2.0;
        mid.y=(pp[x1][l].y+pp[x1][r].y)/2.0;
        double dis=Get_Dis(Center,mid);
        if(epss(Cross(pp[x1][l],Center,pp[x1][r]))<0)
        {
            dis*=-1;
        }
        if(epss(dis-RMAX)>0)
        {
            RMAX=dis;
            xx=i;
        }
    }
    if(flag)
    {
        for(int i=(r+1)%pp[x1].size();i!=l;i=(i+1)%pp[x1].size())
        {
            if(epss(Cross(pp[x1][l],pp[x1][r],pp[x1][i]))==0)
            {
                flag=0;
                break;
            }
            node Center=Get_Center(pp[x1][l],pp[x1][r],pp[x1][i]);
            mid.x=(pp[x1][l].x+pp[x1][r].x)/2.0;
            mid.y=(pp[x1][l].y+pp[x1][r].y)/2.0;
            double dis=Get_Dis(mid,Center);
            if(epss(Cross(pp[x1][l],Center,pp[x1][r]))<0)
            {
                dis*=-1;
            }
            if(epss(dis-LMAX)<0)
            {
                LMAX=dis;
            }
        }
    }
   // cout<<LMAX<<' '<<RMAX<<endl;
    if(flag)
    {
        int tt=n;
        if(x2==1)
            tt=m;
        up(i,0,tt)
        {
            if(epss(Cross(pp[x1][l],pp[x1][r],p[x2][i]))==0&&Judge_mid(pp[x1][l],p[x2][i],pp[x1][r]))
            {
                flagg=0;
                break;
            }
            node Center=Get_Center(pp[x1][l],pp[x1][r],p[x2][i]);
            mid.x=(pp[x1][l].x+pp[x1][r].x)/2.0;
            mid.y=(pp[x1][l].y+pp[x1][r].y)/2.0;
            double dis=Get_Dis(mid,Center);
            //cout << pp[x1][l].x << ' ' << pp[x1][l].y << endl;
			//cout << j << endl;
			//if(j==1)
			//cout << a[j - 1].x << ' ' << a[j - 1].y << endl;
			//cout << pp[x1][r].x << ' ' << pp[x1][r].y << endl;
			//cout << p[x2][i].x << ' ' << p[x2][i].y << endl;
			//cout << "Circle:" << ' ' << Center.x << ' ' << Center.y << endl;
            if (epss(Cross(pp[x1][l],Center,pp[x1][r])*epss(Cross(pp[x1][l],p[x2][i],pp[x1][r]))<0))
            {
                dis*=-1;
            }
            if (epss(Cross(pp[x1][l],p[x2][i],pp[x1][r]))>0)
            {
				if (epss(dis - LMAX)<0)LMAX = dis;
			}
			else if (epss(Cross(pp[x1][l],p[x2][i],pp[x1][r]))<0)
			{
				dis*=-1;
				if (epss(RMAX - dis)<0)RMAX = dis;
			}
			if (epss(LMAX - RMAX) <= 0)
            {
                //cout<<111<<endl;
				flagg=0;
				break;
			}
        }
    }
    else
    {
        flagg=0;
    }
    //cout<<"xxxxx"<<' '<<xx<<endl;
    if(flagg)
        return true;
    if(xx>=0)
    {
        return Judge_Inside(x1,x2,l,xx)||Judge_Inside(x1,x2,xx,r);
    }
    return false;
}


int main()
{
    sfi(n);
    sfi(m);
    up(i,0,n)
    {
        sfd(p[0][i].x);
        sfd(p[0][i].y);
    }
    up(i,0,m)
    {
        sfd(p[1][i].x);
        sfd(p[1][i].y);
    }
    Graham(0);
    Graham(1);
    if(Judge_Inside(0,1,0,pp[0].size()-1))
    {
        printf("YES\n");
    }
    else if(Judge_Inside(1,0,0,pp[1].size()-1))
    {
        printf("YES\n");
    }
    else
    {
        printf("NO\n");
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值