题目大意
给定n个平面上的点,每个点出现的概率都为p,问点最多连线数的期望值。
一个状态下的最多连线数:若出现了点p1,p2,p3…pn,可以在两个点之间连一条线段,两条线段不能在除了端点以外的地方相交,并让连的线段数尽量多。
答案对1e8+7取模。
n<=1000
40%的数据,p=1
分析
先考虑p=1吧。
首先我们看,若在有x个点的情况下,我们连了尽量多的线,那么可以发现全是三角形。这样我们可以搬一个定理
定理:设P为平面上不全部共线的任意N个点组成的一个集合,落在P的凸包边界上的点的个数记作K。则P的任何一个三角剖分(不一定是Delaunay三角剖分)必然由2N−2−K个三角形组成,而且共有3N−3−K条边。
嘿嘿····
其实在考场上怎么考虑呢?我们可以暴力试一试。然后尝试弄出凸包,然后构造一种方案。会发现凸包里每多一个点,原来的某三角形可以分割成新的3个三角形。那么就可以推出来了。
这样的话,设相交线段数为e,我们知道在出现n0个点,其中k0个在凸包上的点的情况下,e=3*n0-3-k0,既然是凸包,我们要特殊考虑0,1,2,发现n0不能为0;1和2没问题。那么
E(e)=E(3∗n−3−k)
,此处的n为读入的n。
要有拆期望分开算的意识。考虑用期望线性律拆它一波咯,但是拆的时候要特别注意,有些特殊情况处理不好,很危险的。不管怎样,先试着拆一拆。
E(e)=3∗E(n−1)−E(k)
E(n-1)是多少呢? n*p-1吧?但是要考虑到n=0的时候,这个情况下e=0,所以这一部分实际上是
3∗(n∗p−1)+3∗(1−p)n
,也就是说,要补上e=0时算错的期望。
再考虑E(k),
E(k)=∑ni=1{P(i为凸包上的点)∗1}
,所以只要看看每个点他作为凸包的点的概率就好了。P之间是互相独立的,这保证了可以
∑
。
考虑一个点i,怎么样让他位于凸包上呢?让他有边连出去就好了!我们枚举这条边,并计算保证这条边一定在凸包上的概率。设这条边连到j,那么我们要保证向量ij的右边没有点出现,左边则随意(当然你反过来算是一样的)。设右边有k个点,那么这条边出现的概率为
p2∗(1−p)k
。k是可以线性维护的,提前把从i出发的向量排好序,弄个指针就好。这样就求出了E(k),做完了。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=1005;
const ll mo=1e8+7;
struct vec
{
double x,y;
}a[N],b[N];
ll st,i,j,k,n,p,ans,f[N],cnt;
double x;
double operator ^(vec a,vec b)
{
return a.x*b.y-a.y*b.x;
}
vec operator -(vec a,vec b)
{
vec c;
c.x=a.x-b.x;
c.y=a.y-b.y;
return c;
}
bool cmp(vec x,vec y)
{
return atan2(x.y,x.x)<atan2(y.y,y.x);
}
int main()
{
freopen("forgive.in","r",stdin);
freopen("forgive.out","w",stdout);
scanf("%lld %lld",&n,&p);
fo(i,1,n)
scanf("%lf %lf",&a[i].x,&a[i].y);
f[0]=1%mo;
fo(i,1,n) f[i]=(f[i-1]*(1-p+mo))%mo;
ans=((ll)3*n*p-3)%mo;
fo(i,1,n)
{
st=0;
fo(j,1,n) if (i!=j)
b[++st]=a[j]-a[i];
sort(b+1,b+1+st,cmp);
k=1;
cnt=0;
fo(j,1,st)
{
while ((b[j]^b[k%st+1])>0&&k%st+1!=j) k=k%st+1,cnt++;
ans=(ans-f[n-2-cnt]*p%mo*p%mo+mo)%mo;
if (cnt) cnt--;
else k++;
}
}
printf("%lld\n",(ans+3*f[n])%mo);
}