BZOJ 2618: [Cqoi2006]凸多边形

这是一道关于半平面交的算法题目,通过将凸多边形拆分成多个向量,利用向量的左侧定义来判断区域。题解参考了Po姐的思路,避免使用双端队列,而是先在一个方向上添加线条,然后在最后删除队首和队尾。关键在于正确计算向量交点和判断点是否位于向量左侧。
摘要由CSDN通过智能技术生成

真●半平面交,半平面交模板题,按照题目描述将每个多边形拆分成n个向量,我们规定向量的左侧为其指定的区域,参照了Po姐的写法,因为双端队列实在是不好搞,我们先在一个方向加线,然后再在最后删队首队尾,值得注意的是用向量求交点和用向量判断一个点是否在这条向量的左面。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
struct point
{
    double x,y;
    point(){}
    point(double _,double __):x(_),y(__){}
    point operator + (point b)
    {
        return point(x+b.x,y+b.y);
    }
    point operator - (point b)
    {
        return point(x-b.x,y-b.y);
    }
    double operator * (point b)
    {
        return x*b.y-y*b.x;
    }
    point operator * (double rate)
    {
        return point(x*rate,y*rate);
    }
}pp[1000];
struct line
{
    point p,v;
    double alpha;
    bool operator < (line b) const
    {
        return alpha<b.alpha;
    }
    line(){}
    line(point a,point b):p(a),v(b-a)
    {
        alpha=atan2(v.y,v.x);
    }
    point operator ^ (line b)
    {
        point mid=p-b.p;
        double rate=(b.v*mid)/(v*b.v);
        return p+v*rate;
    }
}lines[1000000];
int tot;
bool onleft(point p,line l)
{
    point mid=p-l.p;
    return l.v*mid>=0;
}
line dui[1000000];
int top=1,my_final=1;
void get_half_plane_intersection()
{
    sort(lines+1,lines+1+tot);
    for(int i=1;i<=tot;i++)
    {
        while(my_final-top>=2 && !onleft(dui[my_final-1]^dui[my_final-2],lines[i])) my_final--;
        if(my_final-top>=1 && fabs(dui[my_final-1].v*lines[i].v)<=0)
            dui[my_final-1]=onleft(lines[i].p,dui[my_final-1])?lines[i]:dui[my_final-1];
        else dui[my_final++]=lines[i];
    }
    while(1)
    {
        if(my_final-top>=2 && !onleft(dui[my_final-1]^dui[my_final-2],dui[top])) my_final--;
        else if(my_final-top>=2 && !onleft(dui[top]^dui[top+1],dui[my_final-1])) top++;
        else break;
    }
}
int main()
{
    int n,m;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%lf%lf",&pp[i].x,&pp[i].y);
            if(i!=1) lines[++tot]=line(pp[i-1],pp[i]);
        }
        lines[++tot]=line(pp[m],pp[1]);
    }
    get_half_plane_intersection();
    if(my_final-top<=2) printf("0.000\n");
    else
    {
        int sum=0;
        for(int i=top+1;i<my_final;i++) pp[++sum]=dui[i-1]^dui[i];
        pp[++sum]=dui[my_final-1]^dui[top];
        double ans=0;
        for(int i=2;i<=sum;i++) ans+=pp[i-1]*pp[i];
        ans+=pp[sum]*pp[1];
        printf("%.3lf\n",ans/2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值