【NOI2017模拟3.30】原谅(计算几何,期望)

Description

终其一生,我们在寻找一个原谅。
犯下了太多错,要原谅的那个人,永远都是自己。
Samjia在深夜中望见了没有边界的人生,他没有想到过自己犯下了这么多的错误,他想在他的一生中寻求一个原谅。
他的人生是一个没有边界的平面,平面上有n个错误,每个错误是一个点,每个点i有一定的坐标(x[i],y[i]),有一个参数p 表示每个点有p的概率出现在平面上,注意两个不同的点的出现互相没有影响,Samjia可以在两个点之间连一条线段,两条线段不能在除了端点以外的地方相交,现在Samjia想知道他最多可以连的线段数的期望。
温馨提示:请看最后面的提示:)
本题的答案在mod 100000007意义下计算

Description

很显然在点数确定的情况,知道了凸包上的点和凸包内的点可以求出最大的边数。
设边数为A,所有点的个数为B,凸包上的点的个数为C
那么有A=3*B-C-3
然后现在我们要求的是A的期望E(A)
E(A)=E(3BC3) ,这个的意思出现边的期望的总和及 E(a) (枚举边),但是这样不知道怎么求。
根据期望的性质有 E(A)=3E(B)E(C)3
那么这里的E(B)就是出现点的期望总和
=E(0)+E(i)=E(0)+P(i)1=E(0)+np=(1p)n+np
那么现在的关键就是怎样求E(C)凸包上的边出现的期望和。
我们只用单独考虑一条边是否出现在凸包上的期望求可以了
那么我们先枚举一个点,然后再枚举一个点,设它为凸包的下边界,那么它的期望贡献是 (1p)pp1(1)
那么怎样求下面有多少个点: 极角排序一下,然后用叉积找出另一个点与这条边形成最大的钝角,使得这个钝角下面的点都是这条边下面的点。
然后减掉这些贡献就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef double db;
typedef long long ll;
const int maxn=1e5+7,mo=100000007;
ll i,j,k,l,t,n,m,p,tot;
ll c[maxn],ans,ge;
struct node{
    db x,y,z;
}a[maxn],b[maxn];
bool cmp(node x,node y){
    return x.z<y.z;
}
db xiang(db x,db y,db xx,db yy){
    return x*yy-y*xx>0;
}
int main(){
//  freopen("forgive.in","r",stdin);
//  freopen("forgive.out","w",stdout);
    freopen("data.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d%d",&n,&p);c[0]=1;
    fo(i,1,n){
        scanf("%lf%lf",&a[i].x,&a[i].y);
        c[i]=(c[i-1]*(1-p)+mo)%mo;
    }
    ans=3*(n*p+c[n])-3;
    fo(i,1,n){
        tot=0;
        fo(j,1,n){
            if(i!=j){
                b[tot++]=(node){a[j].x-a[i].x,a[j].y-a[i].y,atan2(a[j].y-a[i].y,a[j].x-a[i].x)};
            }
        }
        sort(b,b+tot,cmp);
        k=0;ge=0;
        fo(j,0,tot-1){
            while(xiang(b[j].x,b[j].y,b[(k+1)%tot].x,b[(k+1)%tot].y))k=(k+1)%tot,++ge;
            ans=(ans-c[tot-ge-1]*p%mo*p%mo)%mo;
            if(ge)ge--;else k=(k+1)%tot;
        }
    }
    ans=(ans+mo)%mo;
    printf("%lld\n",ans);
}

你可能感兴趣的:(noi,概率,几何)