bzoj 4481: [Jsoi2015]非诚勿扰 树状数组+数学期望

来源:互联网 发布:天津大学tju网络登录 编辑:程序博客网 时间:2024/05/22 03:50

题意

有n个女性和n个男性。每个女性的如意郎君列表都是所有男性的一个子集,并且可能为空。如果列表非空,她们会在其中选择一个男性作为自己最终接受的对象。将“如意郎君列表”中的男性按照编号从小到大的顺序呈现给她。对于每次呈现,她将独立地以P的概率接受这个男性(换言之,会以1−P的概率拒绝这个男性)。如果她选择了拒绝,App就会呈现列表中下一个男性,以此类推。如果列表中所有的男性都已经呈现,那么会重新按照列表的顺序来呈现这些男性,直到她接受了某个男性为止。显然,在这种规则下,每个女性只能选择接受一个男性,而一个男性可能被多个女性所接受。当然,也可能有部分男性不被任何一个女性接受。这样,每个女性就有了自己接受的男性(“如意郎君列表”为空的除外)。现在考虑任意两个不同的、如意郎君列表非空的女性a和b,如果a的编号比b的编号小,而a选择的男性的编号比b选择的编号大,那么女性a和女性b就叫做一对不稳定因素。求得不稳定因素的期望个数(即平均数目)
1≤N,M≤500,000,0.4≤P<0.6

分析

显然就是要求期望的逆序对个数。
p(x,y)表示第x个女的抽中其列表中的第y个男生的概率。
显然p(x,y)=(1p)y1p+(1p)y+m1p+(1p)y+m21p....

通过无限等比数列求和公式s=a1q,a是首项,q是公比可以得到p(x,y)=(1p)y1p1(1p)m

知道概率后我们只要对数对做一次双关键字排序然后用树状数组求区间和即可。

这题卡精度,要开long double。后来问了别人才知道原来可以先在程序中开long double然后输出的时候再转成double.

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#include <iomanip>using namespace std;const int N=500005;const long double eps=1e-8;int n,m;struct data{int x,y;}a[N];long double gl[N],c[N],p;bool cmp(data a,data b){    return a.x<b.x||a.x==b.x&&a.y<b.y;}void ins(int x,long double y){    while (x<=n)    {        c[x]+=y;        x+=x&(-x);    }}long double find(int x){    long double ans=0;    while (x)    {        ans+=c[x];        x-=x&(-x);    }    return ans;}int main(){    scanf("%d%d",&n,&m);    cin>>p;    for (int i=1;i<=m;i++) scanf("%d%d",&a[i].x,&a[i].y);    sort(a+1,a+m+1,cmp);    int now=0,s=0;    long double w,r,ans=0;    for (int i=1;i<=m;i++)    {        if (a[i].x!=now)        {            for (int j=i-1;j&&a[j].x==now;j--) ins(a[j].y,gl[j]);            now=a[i].x;s=0;w=r=1;            for (s=0;a[i+s].x==now;s++,w*=(1-p));            w=1-w;        }        gl[i]=r*p/w;        r*=(1-p);        ans+=gl[i]*(find(n)-find(a[i].y));    }    printf("%.2lf",(double)ans);    return 0;}
0 0