洛谷4260:博弈論與概率統計(組合數學+莫隊/分塊)

題面
題意:小L在玩遊戲,贏了n場,輸了m場
贏一場得1分,輸一場扣1分
若當前爲0分,則不會扣
問期望得分

前置技能
有一個n個1和m個-1的序列,求前綴和最小值≥0的方案數
考慮不合法的
找到第一個和爲-1的前綴
將其1與-1翻轉
得到一個有n+1 個1和m1 個-1的序列
恰好與不合法的方案一一對應

類比得前綴和最小值恰好爲i 的方案數爲Cn+mn+iCn+mn+i1

考慮n≥m
前綴和最小值爲i貢獻爲n-m+i

經過一輪畫柿子,錯位相減
答案爲(nm)Cn+mn+i=0mCn+mi

f(n+m,m)=i=0mCn+mi

f(i,j)O(1) 推出f(i+1,j)f(i,j1)
故可用莫隊或分塊優化

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const LL p=1e9+7;
const int N=600300,nn=550;

int T,Violet;
LL jc[N],Ijc[N],I[N],ans[N],now=1;
int n[N],m[N];
int L=1,R;

struct yy
{
    int l,r,num;
}f[N];

bool cmp(yy x,yy y)
{
    if((x.l/nn)==(y.l/nn))
    return x.r<y.r;
    return (x.l/nn)<(y.l/nn);
}

LL C(int x,int y)
{
    return jc[x]*Ijc[y]%p*Ijc[x-y]%p;
}

LL IC(int x,int y)
{
    return Ijc[x]*jc[y]%p*jc[x-y]%p;
}

int main()
{
    I[1]=jc[0]=Ijc[0]=1;
    for(int i=2;i<N;i++)
    I[i]=I[p%i]*(p-p/i)%p;

    for(int i=1;i<N;i++)
    jc[i]=jc[i-1]*i%p,Ijc[i]=Ijc[i-1]*I[i]%p;

    cin>>T>>Violet;
    for(int i=1;i<=T;i++)
    {
        f[i].num=i;
        scanf("%d%d",&n[i],&m[i]);
        f[i].l=n[i]+m[i];
        if(n[i]<m[i])
        f[i].r=n[i]-1;
        else
        f[i].r=m[i]-1;
    }

    sort(f+1,f+T+1,cmp);

    for(int i=1;i<=T;i++)
    {
        int l=f[i].l,r=f[i].r;
        while(L<l)
        now=(now+now-C(L,R)+p)%p,L++;
        while(L>l)
        now=(now+C(L-1,R))%p*I[2]%p,L--;
        while(R<r)
        now=(now+C(L,R+1))%p,R++;
        while(R>r)
        now=(now-C(L,R)+p)%p,R--;
        ans[f[i].num]=now;
    }

    for(int i=1;i<=T;i++)
    if(n[i]<m[i])
    printf("%lld\n",ans[i]*IC(n[i]+m[i],n[i])%p);
    else
    printf("%lld\n",(n[i]-m[i]+ans[i]*IC(n[i]+m[i],n[i])%p)%p);

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章