POJ 2451 Uyuw's Concert (半平面交)

题目传送门


题目大意 && 做法

半平面交的模板题,要求nlogn的算法。
需要注意的是atan2的精度很差,排序重载<(cmp)时不要用eps比较,否则会WA。
直接上模板。


代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 20010
#define Eps 1e-10
#define Edge 10000

using namespace std;

int n, cnt;

struct Point{
    double x, y;
    Point() {}
    Point(double _x, double _y):x(_x), y(_y) {}
    friend Point operator + (Point A, Point B){return Point(A.x + B.x, A.y + B.y);}
    friend Point operator - (Point A, Point B){return Point(A.x - B.x, A.y - B.y);}
    friend Point operator * (double A, Point B){return Point(A * B.x, A * B.y);}
    friend Point operator / (Point A, double B){return Point(A.x / B, A.y / B);}
}Poly[N], p[N];

double Dot(Point A, Point B){return A.x * B.x + A.y * B.y;}
double Det(Point A, Point B){return A.x * B.y - A.y * B.x;}

struct Line{
    Point P, v;//点向式
    double ang;
    Line() {}
    Line(Point _P, Point _v):P(_P), v(_v) {ang = atan2(_v.y, _v.x);}
    bool operator < (const Line& l) const{return ang < l.ang;}//注意这里
}L[N], q[N];


bool Onleft(Point A, Line B){
    return Det(A - B.P, B.v) < -Eps;
}

Point Cross(Line A, Line B){
    Point p1 = A.P, q1 = A.P + A.v;
    Point p2 = B.P, q2 = B.P + B.v;
    double x = Det(p2 - p1, q2 - p1), y = Det(q2 - q1, p2 - q1);
    return (x * q1 + y * p1) / (x + y);
}

void HalfplaneI(){
    sort(L+1, L+n+1);//按极角排序

    int head, tail;
    q[head = tail = 0] = L[1];//双端队列
    for(int i = 2; i <= n; i++){
      while(head < tail && !Onleft(p[tail-1], L[i]))  tail --;
      while(head < tail && !Onleft(p[head], L[i]))  head ++;//注意顺序不能换
      q[++tail] = L[i];
      if(fabs(Det(q[tail].v, q[tail-1].v)) < Eps){
        tail --;
        if(Onleft(L[i].P, q[tail]))  q[tail] = L[i];
      }//两向量平行且同向要取内侧的那个
      if(head < tail)  p[tail-1] = Cross(q[tail-1], q[tail]);
    }
    while(head < tail && !Onleft(p[tail-1], q[head]))  tail --;//删除无用平面
    if(tail - head <= 1)  return;//空集
    p[tail] = Cross(q[tail], q[head]);//头尾相交

    cnt = 0;
    for(int i = head; i <= tail; i++)  Poly[++cnt] = p[i];
}

double Get_S(){
    Poly[cnt+1] = Poly[1];
    double S = 0.00;
    for(int i = 1; i <= cnt; i++)
      S += Det(Poly[i], Poly[i+1]);
    return S / 2.00;
}

int main(){
    scanf("%d", &n);

    double x1, y1, x2, y2;
    for(int i = 1; i <= n; i++){
      scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
      L[i] = Line(Point(x1, y1), Point(x2 - x1, y2 - y1));
    }

    L[++n] = Line(Point(0, 0), Point(Edge, 0));
    L[++n] = Line(Point(0, Edge), Point(0, -Edge));
    L[++n] = Line(Point(Edge, 0), Point(0, Edge));
    L[++n] = Line(Point(Edge, Edge), Point(-Edge, 0));//按题意加边框

    HalfplaneI();

    printf("%.1lf\n", Get_S());

    return 0;
}

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值