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

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#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);
}