[JZOJ5036]【NOI2017模拟3.30】原谅

题目大意

给定n个平面上的点,每个点出现的概率都为p,问点最多连线数的期望值。
一个状态下的最多连线数:若出现了点p1,p2,p3…pn,可以在两个点之间连一条线段,两条线段不能在除了端点以外的地方相交,并让连的线段数尽量多。
答案对1e8+7取模。
n<=1000
40%的数据,p=1

分析

先考虑p=1吧。
首先我们看,若在有x个点的情况下,我们连了尽量多的线,那么可以发现全是三角形。这样我们可以搬一个定理

定理:设P为平面上不全部共线的任意N个点组成的一个集合,落在P的凸包边界上的点的个数记作K。则P的任何一个三角剖分(不一定是Delaunay三角剖分)必然由2N2K个三角形组成,而且共有3N3K条边。

嘿嘿····
其实在考场上怎么考虑呢?我们可以暴力试一试。然后尝试弄出凸包,然后构造一种方案。会发现凸包里每多一个点,原来的某三角形可以分割成新的3个三角形。那么就可以推出来了。
这样的话,设相交线段数为e,我们知道在出现n0个点,其中k0个在凸包上的点的情况下,e=3*n0-3-k0,既然是凸包,我们要特殊考虑0,1,2,发现n0不能为0;1和2没问题。那么 E(e)=E(3n3k) ,此处的n为读入的n。
要有拆期望分开算的意识。考虑用期望线性律拆它一波咯,但是拆的时候要特别注意,有些特殊情况处理不好,很危险的。不管怎样,先试着拆一拆。
E(e)=3E(n1)E(k)
E(n-1)是多少呢? n*p-1吧?但是要考虑到n=0的时候,这个情况下e=0,所以这一部分实际上是 3(np1)+3(1p)n ,也就是说,要补上e=0时算错的期望。
再考虑E(k), E(k)=ni=1{P(i)1} ,所以只要看看每个点他作为凸包的点的概率就好了。P之间是互相独立的,这保证了可以
考虑一个点i,怎么样让他位于凸包上呢?让他有边连出去就好了!我们枚举这条边,并计算保证这条边一定在凸包上的概率。设这条边连到j,那么我们要保证向量ij的右边没有点出现,左边则随意(当然你反过来算是一样的)。设右边有k个点,那么这条边出现的概率为 p2(1p)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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值