【UOJ】#242. 【UR #16】破坏蛋糕

传送门http://uoj.ac/problem/242

貌似出题人给的方法一点都不友好QwQ

那个如果一个平面是有限的,那么它被分为两部分,这两部分都是有限的。

假设我们要求的那条直线是y轴(不是也可以转),那么考虑对直线左右分别计算平面是否有限。
就直线右侧而言,我们把交点按照顺序排好。交点 ya yb 之间的区间对应平面是有限的,当且仅当存在 yc yd ,满足 ydybyayc ,且 kc<kd
其中c,d可以是a,b,k代表交点对应直线斜率。

这里写图片描述

感性认识一下吧……

那么先说一下把要求的直线旋到y轴的原因:好判断啊,一边的直线斜率全是连续的了。但是旋转后平行难判,要用原始坐标判断。

那么考虑一下如何维护相交。
我们肯定是维护一个数据结构,然后来去除掉一些我们不需要的信息。那么看看下图所示的情况:

这里写图片描述

如图所示,红线(a)和黑线(b)是已经处理完的直线,然后现在要进行计算的是蓝线(c)
红线和黑线满足于这个关系 yb<ya ka<kb
那么蓝线如果要交黑线,肯定可以交红线,而且交红线可以封闭 [ya,yc] 这一段区间(别吐槽我区间左右反过来的问题,我是从上往下考虑的),封闭的区间比与黑线交封闭的更大。

那么实际上我们只是需要维护一个序列(栈),满足点对应的直线斜率递减,加入序列的顺序是从y坐标大到小依次加入。

当有一条新直线来的时候,在序列中二分能交到哪,给区间头尾打个标记(相当于差分)。
假如一条都交不到,加入序列(说明这条线斜率是目前最小)

直线左侧做法和右侧是完全一样的,但是判断与右侧相反

最后按顺序扫一下,假如直线两侧对应的区间都是有限,说明这段是有限的,反之无限。

#include<stdio.h>
#include<cmath>
#include<algorithm>
#define now e[i].k
#define N 100005
#define eps 0.00000000001

using namespace std;

bool f[N];
int l,r,mid,n,d[N],t,p[N];
long double x1[N],Y1[N],x2[N],y2[N],x,y;
long long XX1[N],YY1[N],XX2[N],YY2[N];

struct mpoi
{
    long double pos;
    int k;
    friend bool operator < (mpoi a,mpoi b){return a.pos>b.pos;}
}e[N];

inline void getpoi(const int &i){e[i].pos=Y1[i]-x1[i]*(Y1[i]-y2[i])/(x1[i]-x2[i]);now=i;}

inline bool cmp(const int &i,const int &j)
{
    return (XX1[i]-XX2[i])*(YY1[j]-YY2[j])!=(XX1[j]-XX2[j])*(YY1[i]-YY2[i]) &&
    (Y1[i]-y2[i])*(x1[j]-x2[j])<(Y1[j]-y2[j])*(x1[i]-x2[i]);
}

inline void judge(const int &f)
{
    d[t=1]=1;
    for (int i=2;i<n;i++) if (f?cmp(e[d[t]].k,now):cmp(now,e[d[t]].k))
    {
         for (l=1,r=t,mid=t+1>>1;l<r;mid=l+r>>1) if (f?cmp(e[d[mid]].k,now):cmp(now,e[d[mid]].k)) r=mid;
         else l=mid+1;
         p[e[d[l]+1].k]++;p[e[i+1].k]--;
    }
    else d[++t]=i;
}

int main()
{
    scanf("%d",&n);n++;
    for (int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld%lld",XX1+i,YY1+i,XX2+i,YY2+i);
        x1[i]=XX1[i];Y1[i]=YY1[i];x2[i]=XX2[i];y2[i]=YY2[i];
    }
    for (int i=1;i<=n;i++) x2[i]-=x1[n],x1[i]-=x1[n],y2[i]-=Y1[n],Y1[i]-=Y1[n];
    long double th=atan((Y1[n]-y2[n])/(x1[n]-x2[n]))+3.14159265358979/2;
    for (int i=1;i<=n;i++)
    {
        x=x1[i]*cos(th)+Y1[i]*sin(th);
        y=Y1[i]*cos(th)-x1[i]*sin(th);
        x1[i]=x;Y1[i]=y;
        x=x2[i]*cos(th)+y2[i]*sin(th);
        y=y2[i]*cos(th)-x2[i]*sin(th);
        x2[i]=x;y2[i]=y;
    }
    for (int i=1;i<n;i++) 
    {
        if (x2[i]<x1[i]) swap(x1[i],x2[i]),swap(Y1[i],y2[i]);
        getpoi(i);
    }
    sort(e+1,e+n);
    judge(1);
    for (int i=1,tag=0;i<=n;p[e[i++].k]=0) if (tag+=p[now]) f[now]=1;
    judge(0);
    for (int i=1,tag=0;i<=n;i++) putchar(((tag+=p[now]) && f[now])?'1':'0'); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值