【CF528E】Triangles 3000(计算几何)

【CF528E】Triangles 3000(计算几何)

题面

CF
平面上有若干条直线,保证不平行,不会三线共点。
求任选三条直线出来围出的三角形的面积的期望。

题解

如果一定考虑直接计算这个三角形的面积,我们很难不去弄出这三个交点。
我们需要的是低于\(O(n^3)\)的复杂度,而\(O(n^3)\)的做法可以直接暴力枚举三条直线。
考虑向量计算面积的方法,对于一个在三角形\(\Delta ABC\)之外的点\(O\),我们可以有:
\[S\Delta ABC=\frac{1}{2}(OA\times OB+OB\times OC+OC\times OA)\]
这个证明不难,画图把每一部分的面积表示出来就很简单了。
接下来枚举一条直线,剩下点按照极角顺序依次加入,然后这个贡献可以拆成三个部分,而我们只算都在枚举的直线上的交点的贡献,在其他直线上的可以在其他时候算。
于是要求的就是这条直线和枚举的直线的交点与前面所有直线与枚举的直线的交点与\(O\)构成的向量的叉积。
叉积是:\((x1,y1)\times (x2,y2)=x1*y2-x2*y1\)
于是得到:\((x1,y1)\times (x2,y2)+(x1,y1)\times (x3,y3)=x1*(y2+y3)-y1*(x2+x3)\)
这个东西显然等于\((x1,y1)\times ((x2,y2)+(x3,y3))\),那么就可以很开心的前缀和了。
注意为了保证顺序正确,需要把所有的直线按照极角提前排序。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAX 3030
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n;double ans;
struct Vect{double x,y;}O,g[MAX];
struct Line{double a,b,c,ang;}L[MAX];
bool operator<(Line a,Line b){return a.ang<b.ang;}
Vect Intersection(Line a,Line b)
{
    if(fabs(a.a)>1e-9)
    {
        double y=(b.c*a.a-a.c*b.a)/(a.b*b.a-a.a*b.b);
        double x=-(a.c+a.b*y)/a.a;
        return (Vect){x,y};
    }
    else
    {
        double x=(b.c*a.b-a.c*b.b)/(a.a*b.b-a.b*b.a);
        double y=-(a.c+a.a*x)/a.b;
        return (Vect){x,y};
    }
}
double Cross(Vect a,Vect b){return a.x*b.y-a.y*b.x;}
Vect operator-(Vect a,Vect b){return (Vect){a.x-b.x,a.y-b.y};}
Vect operator+(Vect a,Vect b){return (Vect){a.x+b.x,a.y+b.y};}
bool cmp(Vect a,Vect b){return Cross(a,b)>=0;}
int main()
{
    n=read();O=(Vect){1e7,1e7};
    for(int i=1;i<=n;++i)
    {
        L[i].a=read(),L[i].b=read(),L[i].c=-read();
        double x,y;
        if(fabs(L[i].b)>1e-7)x=1,y=-L[i].a/L[i].b;
        else y=1,x=-L[i].b/L[i].a;
        L[i].ang=atan2(y,x);
    }
    sort(&L[1],&L[n+1]);
    /*
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
            for(int k=j+1;k<=n;++k)
            {
                Vect g[3];
                g[0]=Intersection(L[i],L[j]);
                g[1]=Intersection(L[j],L[k]);
                g[2]=Intersection(L[k],L[i]);
                ans-=(Cross(g[0],g[1])+Cross(g[1],g[2])+Cross(g[2],g[0]));
            }
    ans/=1.0*n*(n-1)*(n-2)/3;
    printf("%.10lf\n",ans);ans=0;
    */
    for(int i=1;i<=n;++i)
    {
        Vect s=(Vect){0,0};
        for(int j=i%n+1;j!=i;j=j%n+1)
        {
            Vect a=Intersection(L[i],L[j]);
            ans+=Cross(s,a);s=s+a;
        }
    }
    ans/=1.0*n*(n-1)*(n-2)/3;
    printf("%.10lf\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/11163840.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值