Code+三月月赛 Div1 C 博弈论与与概率统计(莫队+组合数+数学期望)

[CODE+]博弈论与概率统计

问题描述

Code+三月月赛 Div1 C 博弈论与与概率统计(莫队+组合数+数学期望)_第1张图片

样例输入

3 500
1 1
2 3
4 4

样例输出

500000004
200000002
728571435

提示

Code+三月月赛 Div1 C 博弈论与与概率统计(莫队+组合数+数学期望)_第2张图片


首先,容易得到题目中的p是没有用的,因为Alice每一种输赢序列的出现概率是相等的,因此只需要算出每种排列的得分之和再除以 Cnn+m C n + m n 即可。

然后将赢记作1,输记作-1,那么得到一个序列 ai a i ,对其求前缀和得到 bi b i
观察可以发现,最终得分等于 aimin(bi) ∑ a i − m i n ( b i ) ,即 nmmin(bi) n − m − m i n ( b i )
那么最终答案等于 (nm)Cnn+mmin(bi) ( n − m ) C n + m n − ∑ m i n ( b i )

考虑求后面的东西,令 F[k] F [ k ] 表示 bi b i 的最小值恰为 k k 的排列数,令 G[k] G [ k ] 表示 bi b i 的最小值小于等于 k k 的排列数
考虑利用折线法来求得 G[k] G [ k ]

注意到最终的 bn+m b n + m 一定是 nm n − m ,那么我们可以认为 ai=1 a i = 1 表示从 (x,y) ( x , y ) 走到 (x+1,y+1) ( x + 1 , y + 1 ) ,而 ai=1 a i = − 1 表示从 (x,y) ( x , y ) 走到 (x+1,y1) ( x + 1 , y − 1 ) ,那么 ai a i 序列就是从 (0,0) ( 0 , 0 ) 走到 (n+m,nm) ( n + m , n − m ) 的一条折线。

考虑 bi b i 的最小值小于等于 k k 的意义,不妨令恰好取得最小值时,前面有 x x 1 1 y y 1 − 1 ,那么有 xy<=k x − y <= k ,反映到折线上就是这条折线至少有一个点的纵坐标小于等于 k k 也就是折线与 y=k y = k 有交。

不妨将折线与 y=k y = k 第一次相交后的部分沿 y=k y = k 对称,对称前后的折线一一对应,那么对称后的折线终点是 (n+m,2kn+m) ( n + m , 2 k − n + m ) ,那么此时对应的 ai a i 中有 k+m k + m 个1, nk n − k 个-1,且这样的折线有 Cm+kn+m C n + m m + k 条,对应的 ai a i 序列也就有这么多个。

因此我们得到 G[k]=Cm+kn+m G [ k ] = C n + m m + k ,进一步的 F[k]=G[k]G[k1]=Cm+kn+mCm+k1n+m F [ k ] = G [ k ] − G [ k − 1 ] = C n + m m + k − C n + m m + k − 1

为了得到答案,我们考虑 min(bi) m i n ( b i ) 的取值范围
容易得到 n>=mmin(bi)[m,0] n >= m 时 , m i n ( b i ) ∈ [ − m , 0 ] ,当 n<mmin(bi)[m,nm] n < m 时 , m i n ( b i ) ∈ [ − m , n − m ] ,因此需要分类讨论

先考虑 n>=m n >= m 时,得到 Ans=(nm)Cnn+m0k=mk(Cm+kn+mCm+k1n+m) A n s = ( n − m ) C n + m n − ∑ k = − m 0 k ( C n + m m + k − C n + m m + k − 1 ) ,推导一番

Ans=(nm)Cnn+m+k=0mk(Cmkn+mCmk1n+m)=(nm)Cnn+m+k=0mkCmkn+mk=0m1kCm1kn+m A n s = ( n − m ) C n + m n + ∑ k = 0 m k ( C n + m m − k − C n + m m − k − 1 ) = ( n − m ) C n + m n + ∑ k = 0 m k C n + m m − k − ∑ k = 0 m − 1 k C n + m m − 1 − k

Ans=(nm)Cnn+m+k=0m1Cm1kn+m=(nm)Cnn+m+k=0m1Ckn+m A n s = ( n − m ) C n + m n + ∑ k = 0 m − 1 C n + m m − 1 − k = ( n − m ) C n + m n + ∑ k = 0 m − 1 C n + m k

我们发现是一个组合数前缀和的形式,不妨再看看 n<m n < m 时的情况。类似的处理
Ans=(nm)Cnn+mk=mnmk(Cm+kn+mCm+k1n+m)=(nm)Cmn+m+k=mnmkCmkn+mk=mnm1kCm1km+n A n s = ( n − m ) C n + m n − ∑ k = − m n − m k ( C n + m m + k − C n + m m + k − 1 ) = ( n − m ) C n + m m + ∑ k = m − n m k C n + m m − k − ∑ k = m − n m − 1 k C m + n m − 1 − k

Ans=(nm)Cnn+m+(mn)Cnn+m+k=mnm1Cm1kn+m=k=0n1Ckn+m A n s = ( n − m ) C n + m n + ( m − n ) C n + m n + ∑ k = m − n m − 1 C n + m m − 1 − k = ∑ k = 0 n − 1 C n + m k

我们发现同样是一个组合数前缀和的形式,那么接下来考虑如何求组合数前缀和。注意到

k=0mCkn+1=k=0m(Ckn+Ck1n)=2k=0mCknCmn ∑ k = 0 m C n + 1 k = ∑ k = 0 m ( C n k + C n k − 1 ) = 2 ∑ k = 0 m C n k − C n m

因此,如果我们已知 mk=0Ckn ∑ k = 0 m C n k ,可以 O(1) O ( 1 ) 得到 mk=0Ckn+1 ∑ k = 0 m C n + 1 k ,反之亦然

因此我们可以用莫队算法来处理多次询问。总时间复杂度 O((N+M)N+M) O ( ( N + M ) N + M )


代码:

#include
#include
#include
#include
#include
#define N 250005
using namespace std;
const int mod=1e9+7,inv2=1000000008>>1;
int id[N],T,p,fac[N],inv[N],Ans;
struct node{int id,l,r;}K[N],A[N];
bool operator<(node a,node b)
{
    if(id[a.l]==id[b.l])return a.rreturn id[a.l]int &x,int y){x+=y;x-=x>=mod?mod:0;}
void sub(int &x,int y){x-=y;x+=x<0?mod:0;}
int mul(int x,int y){return 1ll*x*y%mod;}
int C(int n,int m)
{
    if(n<m)return 0;
    return mul(fac[n],mul(inv[m],inv[n-m]));
}
int inv_C(int n,int m)
{
    return mul(inv[n],mul(fac[m],fac[n-m]));
}
void UD1(int n,int m)
{
    Ans=mul(Ans,2);
    sub(Ans,C(n,m));
}
void UD2(int n,int m)
{
    add(Ans,C(n-1,m));
    Ans=mul(Ans,inv2);
}
void UD3(int n,int m)
{
    add(Ans,C(n,m+1));
}
void UD4(int n,int m)
{
    sub(Ans,C(n,m));
}
void Solve()
{
    int i,j,k,n,m,S=477;
    for(i=1;isort(K+1,K+T+1);
    n=0;m=0;Ans=1;
    for(i=1;i<=T;i++)
    {
        while(nm);
        while(n>K[i].l)UD2(n--,m);
        while(mm++);
        while(m>K[i].r)UD4(n,m--);
        add(A[K[i].id].id,Ans);
    }
}
int main()
{
    int i,j,k,n,m;
    fac[0]=fac[1]=inv[0]=inv[1]=1;
    for(i=2;i1],i);
        inv[i]=mul(inv[mod%i],mod-mod/i);
    }
    for(i=2;i1]);
    scanf("%d%d",&T,&p);
    for(i=1;i<=T;i++)
    {
        scanf("%d%d",&n,&m);
        A[i].l=n;A[i].r=m;
        if(n>=m)A[i].id=mul(n-m,C(n+m,n)),K[i]=(node){i,n+m,m-1};
        else K[i]=(node){i,n+m,n-1};
    }
    Solve();
    for(i=1;i<=T;i++)
    {
        A[i].id=mul(A[i].id,inv_C(A[i].l+A[i].r,A[i].l));
        printf("%d\n",A[i].id);
    }
}

你可能感兴趣的:(莫队,数学杂题,概率与期望)