【题解】Triangles 3000 [CF528E]

【题解】Triangles 3000 [CF528E]

传送门:\(\text{Triangles 3000 [CF528E]}\)

【题目描述】

给出 \(n\) \((n \leqslant 3000)\) 条直线的方程 \(ax+by=c\)(不会有三线交于一点的情况),求任选三条直线组成的三角形面积期望值。

【分析】

【学习笔记】计算几何全家桶

\(\frac{\sum S_{\Delta ABC}}{C_{n}^{3}}\)

\(\text{naive}\) 的想法是暴力枚举三条直线计算交点,再乱搞算面积,似乎无法优化。

考虑多边形面积的一般式:\(S=\frac{1}{2}\sum_{i\in[1,n]}\vec{OP_i}\times \vec{OP}_{i\%n+1}\) (点集 \(\{P_1,P_2...P_n\}\) 呈逆时针排列,\(O\) 为平面上任意一点)

放到三角形中就是 \(S=\frac{1}{2}(\vec{OA}\times \vec{OB}+\vec{OB}\times \vec{OC}+\vec{OC}\times \vec{OA})\)

即对于三角形的每条边 \(EF\)(设 \(\vec{EF}\) 左方为三角形内部),\(\vec{OE}\) 叉乘 \(\vec{OF}\) 再求和。

考虑枚举一条直线,设法统计该直线上所有需要计算的 “\(\vec{EF}\)” 。

如果将其余的直线按极角从小到大依次加入,对于当前要加入的直线 \(Line\),设 \(EF\)\(Line\) 交点为 \(P\),前面已经加入的直线与 \(Line\) 交点的集合为 \(\{Q_1,Q_2...Q_m\}\),可知对于任意的 \(Q_{i\in[1,m]}\)\(\vec{Q_iP}\) 均是需要计算的(自己画个图yy下,方向在三角形中均为逆时针,而且特殊情况都被题意中的“不会有三线交于一点”干掉了),所以答案需要累加 \(\sum_{i=1}^{m}OQ_i \times OP\),将叉积式子拆开,得到 \(\sum_{i=1}^{m}({Q_i}_{x}*P_{y}-{Q_i}_{y}*P_{x})\) \(=(\sum_{i=1}^{m}{Q_i}_{x})*P_{y}-(\sum_{i=1}^{m}{Q_i}_{y})*P_{x}\),直接记录下已经求出的 \(Q_i\) 坐标值总和即可。

注意最后答案要除以 \((2C_{n}^{3}=\frac{n(n-1)(n-2)}{3})\)

时间复杂度:\(O(n^2)\)

(没有卡精度好评)

【Code】

#include<algorithm>
#include<cstdio>
#include<cmath>
#define LD double
#define LL long long
#define Re register int
#define Vector Point
using namespace std;
const int N=3003;
const LD eps=1e-8;
inline int dcmp(LD a){return a<-eps?-1:(a>eps?1:0);}//处理精度
struct Point{
    LD x,y;Point(LD X=0,LD Y=0){x=X,y=Y;}
    inline void in(){scanf("%lf%lf",&x,&y);}
    inline void print(){printf("%.3lf %.3lf\n",x,y);}
};
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
inline LD Dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;}//【点积】
inline LD Cro(Vector a,Vector b){return a.x*b.y-a.y*b.x;}//【叉积】
inline LD Len(Vector a){return sqrt(Dot(a,a));}//【模长】
inline Vector operator+(Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
inline Vector operator-(Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
inline Vector operator*(Vector a,LD b){return Vector(a.x*b,a.y*b);}
inline Point cross_LL(Point a,Point b,Point c,Point d){//【两直线AB,CD的交点】
    Vector x=b-a,y=d-c,z=a-c;
    return a+x*(Cro(y,z)/Cro(x,y));//点A加上向量AF
}
inline int judge(Point a,Point L,Point R){//判断AL是否在AR右边
    return dcmp(Cro(L-a,R-a))>0;//必须严格以内
}
struct Line{
    Point a,b;LD k;Line(Point A=Point(0,0),Point B=Point(0,0)){a=A,b=B,k=atan2(b.y-a.y,b.x-a.x);}
    inline bool operator<(const Line &O)const{return dcmp(k-O.k)?dcmp(k-O.k)<0:judge(O.a,O.b,a);}//如果角度相等则取左边的
}L[N];
inline Point cross(Line L1,Line L2){return cross_LL(L1.a,L1.b,L2.a,L2.b);}//获取直线L1,L2的交点
int n,a,b,c;LD x,y,ans;
int main(){
//    freopen("123.txt","r",stdin);
    in(n);
    for(Re i=1;i<=n;++i){
        in(a),in(b),in(c);
        if(!dcmp(a))L[i]=Line(Point(0,(LD)c/b),Point(1,(LD)c/b));//by=c -> y=c/b
        else if(!dcmp(b))L[i]=Line(Point((LD)c/a,0),Point((LD)c/a,1));//ax=c -> x=c/a
        else L[i]=Line(Point(0,(LD)c/b),Point(1,(LD)(c-a)/b));//ax+by=c -> y=(c-a*x)/b
    }
    sort(L+1,L+n+1);
    for(Re i=1;i<=n;++i){
        Point S,P;
        for(Re j=i<n?i+1:1;j!=i;j=j<n?j+1:1)//注意这里应该从i的下一个开始枚举
            P=cross(L[i],L[j]),ans+=Cro(S,P),S=S+P;
    }
    printf("%lf\n",ans*3/n/(n-1)/(n-2));
}
posted @ 2020-04-11 15:28  辰星凌  阅读(222)  评论(0编辑  收藏  举报