Problem A: 踢罐子 解题报告

Problem A: 踢罐子

Description

平面上有(n)个点,其中任意2点不重合,任意3点不共线。
我们等概率地选取一个点A,再在剩下的(n-1)个点中等概率地选取一个点B,再在剩下的(n-2)个点中等概率地选取一个点C。
然后我们计算伤害倍率(d)。作ABC外接圆,每一个位于弧BC和线段BC之间的点计1倍,每一个位于弧BC上的点(包括B,C两点)计1/2倍,特别的,点A计1倍。将这些倍率全部加起来得到伤害倍率(d)
注意:弧BC是指,ABC外接圆上B到C而不包含点A的部分,这个弧不一定是劣弧。
给定这(n)个点的坐标,你需要求出(d)的期望。为了简单起见,你只需要输出(d imes n imes (n-1) imes (n-2) imes 2)的值,可以看出这是一个整数。

Input

第一行一个正整数(n)
接下来(n)行,每行2个整数(x, y),空格分隔,表示一个坐标。

Output

仅一行,一个数,表示(d imes n imes (n-1) imes (n-2) imes 2)的值。

HINT

对于40%的数据,n<=20。
对于60%的数据,n<=50。
对于80%的数据,n<=200。
对于100%的数据,3<=n<=1000。
对于100%的数据,xi和yi的绝对值不超过2000000000。


暴力需要求圆心,最后选择了列圆的方程高斯消元解的诡异办法,wjyyy神仙给出了一个向量的做法还没仔细看...

事实上这个题感觉思路不太自然。

先把处于顶点的贡献算好是(4n(n-1)(n-2))

然后四个点四个点讨论

发现如果四个点是凸边形,那么这四个点贡献8,否则不产生贡献。

问题转化成统计凸四边形个数了。

凸四边形两两连边后有4条边剩下两个点在异侧,2条边同侧

而凹的3异3同

可以先统计X个异侧,Y个同侧,然后列方程解凸四边形个数

统计的时候枚举一个点,然后极角排序另一个就可以了。


Code:

#include <cstdio>
#include <algorithm>
#include <cmath>
#define ll long long
const int N=2010;
const double pi=acos(-1);
int n,tot;ll X,Y;
double dx[N],dy[N],s[N];
ll cal(ll x){return x*(x-1)/2;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lf%lf",dx+i,dy+i);
    for(int i=1;i<=n;i++)
    {
        tot=0;
        for(int j=1;j<=n;j++)
        {
            if(i!=j)
            {
                s[++tot]=atan2(dy[j]-dy[i],dx[j]-dx[i]);
                if(s[tot]<0) s[tot]+=pi*2;
            }
        }
        std::sort(s+1,s+1+tot);
        for(int j=1;j<=tot;j++) s[j+tot]=s[j]+2*pi;
        for(int j=1,k=1;j<=tot;j++)
        {
            while(k<=tot*2&&s[k]-s[j]<=pi) ++k;
            int c1=k-j-1,c2=n-c1-2;
            X+=1ll*c1*c2;//异
            Y+=cal(c1)+cal(c2);//同
        }
    }
    printf("%lld
",(Y-X)*2+4ll*n*(n-1)*(n-2));
    return 0;
}

2018.12.31

原文地址:https://www.cnblogs.com/butterflydew/p/10203260.html