bzoj 4445: [Scoi2015]小凸想跑步 (半平面交)

题目描述

传送门

题目大意:一个凸n边形,N个顶点按照逆时针从0~n-l编号。随机站在凸多边形内的某个位置,标记为
P点。将P点与n个顶点各连一条边,形成N个三角形。求P点,0号点,1号点形成的三角形的面
积是N个三角形中最小的一个的概率。

题解

nlogn半平面交。
主要就是化简出解析式,然后用半平面交求解不等式组。
可以用叉积表示三角形的面积,注意别叉反了,要么三角形的面积就是负的了。
设P(x,y),可以得到
(xa[i].x,ya[i].y)×(xa[i+1].x,ya[i+1].y)>(xa[0].x,ya[0].y)×(xa[1].x,ya[1].y)
(a[i].ya[i+1].y)x+(a[i+1].xa[i].x)y+a[i].xa[i+1].ya[i].ya[i+1].x>(a[0].ya[1].y)x+(a[1].xa[0].x)y+a[0].xa[1].ya[0].ya[1].x
移项得
(a[i].ya[i+1].ya[0].y+a[1].y)x+(a[i+1].xa[i].xa[1].x+a[0].x)y+a[i].xa[i+1].ya[i].ya[i+1].xa[0].xa[1].y+a[0].ya[1].x>0
a=(a[i].ya[i+1].ya[0].y+a[1].y),b=(a[i+1].xa[i].xa[1].x+a[0].x),c=a[i].xa[i+1].ya[i].ya[i+1].xa[0].xa[1].y+a[0].ya[1].x
那么方程就化简成了 ax+by+c>0 的形式。
我们要用一个向量表示这条解析式,并且让向量的左边表示 ax+by+c>0 的部分。
我们选择在向量上的两个点,如果b>0,选取的两个点p1,p2的横坐标递增;如果b<0,选取的两个点的横坐标递减。向量 v=p2p1
然后直接做 O(nlogn) 的半平面交就可以了。
需要注意的的细节就是a=0,b=0,a=0且b=0,这些情况需要特判。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 200003
#define eps 1e-15
using namespace std;
const double inf=1e17;
int n,m,cnt,head,tail;
struct data{
    double x,y; 
    data(double X=0,double Y=0) {
        x=X,y=Y;
    }
}s[N],p1[10],p[N],poly[N];
data operator +(data a,data b){return data(a.x+b.x,a.y+b.y);}
data operator -(data a,data b){return data(a.x-b.x,a.y-b.y);}
data operator *(data a,double t){return data(a.x*t,a.y*t);}
data operator /(data a,double t){return data(a.x/t,a.y/t);}
struct line{
    data p,v; double ang;
    line(data a=data(),data b=data()){
        p=a; v=b-a;
        ang=atan2(v.y,v.x);
    }
}l[N],q[N];
bool operator <(line a,line b){return a.ang<b.ang;}
void calc(int i,int j,double &a,double &b,double &c)
{
    a=s[i].y-s[j].y;
    b=s[j].x-s[i].x;
    c=s[i].x*s[j].y-s[i].y*s[j].x;
}
void init()
{
    p1[1]=data(inf,inf); p1[2]=data(-inf,inf); p1[3]=data(-inf,-inf); p1[4]=data(inf,-inf);
    l[++cnt]=line(p1[1],p1[2]);
    l[++cnt]=line(p1[2],p1[3]);
    l[++cnt]=line(p1[3],p1[4]);
    l[++cnt]=line(p1[4],p1[1]);
}
double cross(data a,data b)
{
    return a.x*b.y-a.y*b.x;
}
data glt(line a,line b)
{
    data v=a.p-b.p;
    double t=cross(b.v,v)/cross(a.v,b.v);
    return a.p+a.v*t;
}
int dcmp(double x)
{
    if (fabs(x)<eps) return 0;
    return x>0?1:-1;
}
bool Onleft(line a,data p)
{
    return dcmp(cross(a.v,p-a.p))>=0;
} 
void halfpins()
{
    sort(l+1,l+cnt+1);
    q[1]=l[1]; head=tail=1;
    for (int i=2;i<=cnt;i++) {
        while (head<tail&&!Onleft(l[i],p[tail-1])) tail--;
        while (head<tail&&!Onleft(l[i],p[head])) head++;
        q[++tail]=l[i];
        if (fabs(cross(q[tail].v,q[tail-1].v))<0) {
            tail--;
            if (Onleft(q[tail],l[i].p)) q[tail]=l[i];
        }
        if (head<tail)p[tail-1]=glt(q[tail],q[tail-1]);
    }
    while (head<tail&&!Onleft(q[head],p[tail-1])) tail--;
    if (tail-head<=1) return;
    p[tail]=glt(q[tail],q[head]);
    for (int i=head;i<=tail;i++)
     poly[++m]=p[i];
    poly[m+1]=poly[1];
}
double area(data a[],int n)
{
    double ans=0;
    for (int i=2;i<=n-1;i++)
     ans+=cross(a[i]-a[1],a[i+1]-a[1]);
    return ans;
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lf%lf",&s[i].x,&s[i].y);
    s[n+1]=s[1];
    double squ=area(s,n);
    for (int i=1;i<=n;i++) l[++cnt]=line(s[i],s[i+1]);
    double a,b,c; double a1,b1,c1; double a2,b2,c2;
    calc(1,2,a1,b1,c1);
    bool pd=true;
    for (int i=2;i<=n;i++) {
        calc(i,i+1,a2,b2,c2);
        a=a2-a1; b=b2-b1; c=c2-c1;
        if (a==0&&b==0&&c<0) {
            pd=false;
            break;
        }
        if (b==0) {
            double t=-c/a;
            if (a>0) l[++cnt]=line(data(t,0),data(t,-1));
            else l[++cnt]=line(data(t,0),data(t,1));
            continue;
        }
        if (a==0) {
            double t=-c/b;
            if (b>0) l[++cnt]=line(data(0,t),data(1,t));
            else l[++cnt]=line(data(0,t),data(-1,t));
            continue;
        }
        data p1,p2; p1.x=1; p2.x=2;
        p1.y=(-c-a*p1.x)/b;
        p2.y=(-c-a*p2.x)/b;
        if (b<0) swap(p1,p2);
        l[++cnt]=line(p1,p2);
    }
    init();
    halfpins();
    if (!pd||m==0) {
        printf("0.0000\n");
        return 0;
    }
    double s1=area(poly,m);
    printf("%.4lf\n",s1/squ);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值