[SCOI2015]小凸想跑步-题解

题目地址-loj

题意见题面。

我们知道,在计算几何中,我们可以用叉积来表示三角形面积,于是我们可以设站的点为 ( x , y ) (x,y) (x,y),凸边形的顶点为 ( x 0 , y 0 ) ∼ ( x n − 1 , y n − 1 ) (x_0,y_0)\sim (x_{n-1},y_{n-1}) (x0,y0)(xn1,yn1),那么根据题意我们可以得到 n − 1 n-1 n1个不等式:

( x 0 − x ) ( y 1 − y ) − ( y 0 − y ) ( x 1 − x ) ≤ ( x k − x ) ( y k + 1 − y ) − ( y k − y ) ( x k + 1 − x ) (x_0-x)(y_1-y)-(y_0-y)(x_1-x)\leq (x_k-x)(y_{k+1}-y)-(y_k-y)(x_{k+1}-x) (x0x)(y1y)(y0y)(x1x)(xkx)(yk+1y)(yky)(xk+1x)

将其化简为 a x + b x + c = 0 ax+bx+c=0 ax+bx+c=0的形式:

( y 0 − y 1 − y k + y k + 1 ) x + ( x 1 − x 0 − x k + 1 + x k ) y + ( x 0 y 1 − x 1 y 0 − x k y k + 1 + x k + 1 y k ) ≤ 0 (y_0-y_1-y_k+y_{k+1})x+(x_1-x_0-x_{k+1}+x_k)y+(x_0y_1-x_1y_0-x_ky_{k+1}+x_{k+1}y_k)\leq 0 (y0y1yk+yk+1)x+(x1x0xk+1+xk)y+(x0y1x1y0xkyk+1+xk+1yk)0

然后用半平面交就可以求出解的范围大小,用交的面积除以凸边形总面积便是答案。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define db double
using namespace std;
const int M=2e5+10;
const db eps=1e-10;
int dcmp(db x){if(fabs(x)<eps) return 0;return x<0?-1:1;}
int n,cnt;
db intot,ans;
struct point{
    db x,y;
    void in(){scanf("%lf%lf",&x,&y);}
    point(){}
    point(db a,db b):x(a),y(b){}
}p[M];
point operator +(point a,point b){return point(a.x+b.x,a.y+b.y);}
point operator -(point a,point b){return point(a.x-b.x,a.y-b.y);}
point operator *(point a,db b){return point(a.x*b,a.y*b);}
point operator /(point a,db b){return point(a.x/b,a.y/b);}
db cross(point a,point b){return a.x*b.y-a.y*b.x;}
struct line{
    point a,v;
    db arc;
	line(){}
    line(point x,point y):a(x),v(y){arc=atan2(v.y,v.x);}
    bool operator <(line b)const{
       if(fabs(arc-b.arc)>eps)
       return arc<b.arc;
       return cross(v,b.a-a)<0;
    }
}l[M],q[M];
point getcut(line a,line b){
    point u=a.a-b.a;
    db t=cross(b.v,u)/cross(a.v,b.v);
    return a.a+a.v*t;
}
bool isonleft(line a,point b){return cross(a.v,b-a.a)>0;}
int halfcross(){
    int fi=1,la=1;
    q[1]=l[1];p[1]=p[0]=p[2]=p[3]=point(0,0);
    for(int i=2;i<=cnt;i++){
        while(fi<la&&!isonleft(l[i],p[la-1])) la--;
        while(fi<la&&!isonleft(l[i],p[fi])) fi++;
        q[++la]=l[i];
        if(fi<la) p[la-1]=getcut(q[la-1],q[la]);
    }
    while(fi<la&&!isonleft(q[fi],p[la-1])) la--;
    p[la]=getcut(q[la],q[fi]);
    for(int i=fi;i<=la;i++)p[i-fi+1]=p[i];
    p[la-fi+2]=p[1];
    return la-fi+1;
}

int main(){
	freopen("convex.in","r",stdin);
	freopen("convex.out","w",stdout);
    scanf("%d",&n);
    p[1].in();p[2].in();p[n+1]=p[1];
    db tt=p[1].y*p[2].x-p[1].x*p[2].y;
    for(int i=3;i<=n+1;i++){
        if(i<=n)p[i].in();
        int j=i-1;
        db a=p[1].y-p[2].y-p[j].y+p[i].y;
        db b=p[2].x-p[1].x-p[i].x+p[j].x;
        db c=tt+p[j].x*p[i].y-p[i].x*p[j].y;c*=-1;
        l[++cnt]=line(fabs(a)>eps?point(-c/a,0):point(0,-c/b),point(-b,a));
    }
    for(int i=1;i<=n;i++) intot+=cross(p[i],p[i+1]);
    for(int i=1;i<=n;i++) l[++cnt]=line(p[i],p[i+1]-p[i]);
    int tot=0;
    sort(l+1,l+cnt+1);
    for(int i=1;i<=cnt;i++){
        if(dcmp(l[i].arc-l[i-1].arc)!=0||i==1) l[++tot]=l[i];
    }   cnt=tot;tot=halfcross();
    if(tot<3){printf("0.0000\n");return 0;}
    for(int i=1;i<=tot;i++)ans+=cross(p[i],p[i+1]);
    intot=fabs(intot);ans=fabs(ans);
    printf("%.4lf\n",ans/intot);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VictoryCzt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值