bzoj4445: [Scoi2015]小凸想跑步

简述

  面积的大小关系可以用叉积的形式列出一些不等式,然后为了保证点在多边形内还要把每条边看做半平面加进去。最后做一遍半平面交。
  形如 ax+by+c>0 的不等式,可以通过如下方法来构造一个半平面。

vec make(double a, double b, double c)
{
    double x, y;
    x=fabs(b)>eps?0:-c/a;
    y=fabs(b)>eps?-c/b:0;
    return vec(point(x,y),b,-a);
}

  然后就叉积算算面积,再输出比值就好了。

代码

//半平面交 
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 300000
#define eps 1e-8
#define finf 1e60
using namespace std;
struct point
{
    double x, y;
    point(double x, double y):x(x),y(y){}
    point(){}
}pt[maxn], _;
struct vec
{
    point pt;
    double x, y, th;
    vec(point pt, double x, double y):pt(pt),x(x),y(y){}
    vec(){}
}seg[maxn], q[maxn];
int N, tot, x[maxn], y[maxn];
double a[maxn], b[maxn], S1, S2;
point operator+(point p, vec v){return point(p.x+v.x,p.y+v.y);}
vec operator-(point p1, point p2){return vec(p2,p1.x-p2.x,p1.y-p2.y);}
double operator*(vec v1, vec v2){return v1.x*v2.y-v1.y*v2.x;}
vec operator+(vec v1, vec v2){return vec(_,v1.x+v2.x,v1.y+v2.y);}
vec operator-(vec v1, vec v2){return vec(_,v1.x-v2.x,v1.y-v2.y);}
vec operator*(vec v1, double t){return vec(v1.pt,v1.x*t,v1.y*t);}
bool in(point p, vec v){return (p-v.pt)*v<eps;}
bool operator<(const vec &v1, const vec &v2)
{return fabs(v1.th-v2.th)<-eps?in(v1.pt,v2):v1.th<v2.th;}
point inter(vec v1, vec v2)
{
    vec u=v1.pt-v2.pt; double t=v2*u/(v1*v2);
    return v1.pt+v1*t;
}
vec make(double a, double b, double c)
{
    double x, y;
    x=fabs(b)>eps?0:-c/a;
    y=fabs(b)>eps?-c/b:0;
    return vec(point(x,y),b,-a);
}
void init()
{
    int i;
    scanf("%d",&N);
    for(i=0;i<N;i++)scanf("%d%d",x+i,y+i);
    for(i=0;i<N;i++)a[i]=x[(i+1)%N]-x[i], b[i]=y[(i+1)%N]-y[i];
    for(i=1;i<N;i++)
        seg[++tot]=make(b[0]-b[i],a[i]-a[0],a[0]*y[0]-a[i]*y[i]+b[i]*x[i]-b[0]*x[0]);
    for(i=0;i<N;i++)seg[++tot]=point(x[(i+1)%N],y[(i+1)%N])-point(x[i],y[i]);
}
void hpi()
{
    int l, r, i, x;
    for(i=1;i<=tot;i++)seg[i].th=atan2(seg[i].y,seg[i].x);
    sort(seg+1,seg+tot+1);
    for(x=1,i=2;i<=tot;i++)if(fabs(seg[i].th-seg[i-1].th)>eps)seg[++x]=seg[i];tot=x;
    q[l=r=1]=seg[1];
    for(i=2;i<=tot;i++)
    {
        while(l<r and !in(inter(q[r],q[r-1]),seg[i]))r--;
        while(l<r and !in(inter(q[l],q[l+1]),seg[i]))l++;
        q[++r]=seg[i];
    }
    while(l<r and !in(inter(q[r],q[r-1]),q[l]))r--;
    while(l<r and !in(inter(q[l],q[l+1]),q[r]))l++;
    tot=0;
    for(i=l;i<r;i++)pt[++tot]=inter(q[i],q[i+1]);
    pt[++tot]=inter(q[r],q[l]);
}
void calc()
{
    int i;
    point p0(x[0],y[0]);
    for(i=3;i<=tot;i++)S1+=fabs((pt[i]-pt[1])*(pt[i-1]-pt[1]));
    for(i=2;i<N;i++)S2+=fabs((point(x[i],y[i])-p0)*(point(x[i-1],y[i-1])-p0));
    printf("%.4lf",S1/S2);
}
int main()
{
    init();
    hpi();
    calc();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值