zoukankan      html  css  js  c++  java
  • [CodePlus 2018 3 月赛] 博弈论与概率统计

    link

    题意简述

    小 $A$ 与小 $B$ 在玩游戏,已知小 $A$ 赢 $n$ 局,小 $B$ 赢 $m$ 局,没有平局情况,且赢加一分,输减一分,而若只有 $0$ 分仍输不扣分。

    已知小 $A$ 每次赢得概率为 $p$ ,问小 $A$ 得分期望。 $T$ 组数据。

    $T,n,mleq 2.5 imes 10^5$

    $solution:$

    因为赢场输场已经固定,所以 $p$ 其实是没有用,则现在考虑计算小 $A$ 得分总和。

    将赢输场前缀和,记为 ${s}$,则得分为 $n-m-min{s}$ 。假设所有 $-1$ 均有意义,则分数为 $n-m$ 。

    设 $min{s}=x$,则第一次出现 $-1,-2,…,x$ 均无意义因为当时得分前为 $0$ 而又被 $-1$,无意义,共出现 $|x|$ 次情况,即得分为 $n-m-min{s}$。

    所以现在的问题转化为给定 $n$ 个 $1$ 与 $m$ 个 $-1$ ,问最小前缀和为 $w$ 的方案数。

    基本操作,将问题转化为平面移动问题。

    若要求最小前缀和为 $w$ 的方案数,可以表示为从 $(0,0)$ 走到 $(n,m)$ 的方案数,每次往上或左走一步求经过 $y=x+w$ 但不能经过 $y=x+w+1$ 直线的方案数。

    考虑求从 $(1,1)$ 到 $(n,m)$ 经过 $y=x+w$ 的方案数,可以将第一次经过 $y=x+w$ 的交点之前部分对 $y=x+w$ 对称,则 $(0,0)$ 对称到 $(-w,w)$ ,可以发现每次从 $(-w,w)$ 到 $(n,m)$ 经交点对称后对应一条合法路径,则其方案数为 $dbinom{n+m}{n+w}$ 。

    通过简单容斥原理得到若最小前缀和为 $w​$ 的方案数为 $dbinom{n+m}{n+w}-dbinom{n+m}{n+w+1}​$ 。

    考虑赢 $n​$ 场输 $m​$ 场的得分,得分区间为 $[max{0,n-m},n]​$ 。

    分类讨论 $n,m$ 大小。

    若 $ngeq m$ ,则得分区间在 $[n-m,n]$ , $min{s}in [-m,0]$。

    $$Ans=sum_{i=0}^{m} (n-m+i) (dbinom{n+m}{n+i}-dbinom{n+m}{n+i+1})\=(n-m) sum_{i=0}^m(dbinom{n+m}{n+i}-dbinom{n+m}{n+i+1}) +sum_{i=0}^m i imes (dbinom{n+m}{m-i}-dbinom{n+m}{m-i-1})\=(n-m)dbinom{n+m}{n}+sum_{i=0}^{m-1} dbinom{n+m}{i}​$$

    若 $n<m​$ ,则同理 $min{s}in [{-m,n-m}]​$

    $$Ans=sum_{i=m-n}^m (n-m+i) imesdbinom{n+m}{m-i}-dbinom{n+m}{m-i-1}\=(n-m)dbinom{n+m}{n}+sum_{i=m-n}^m i imes dbinom{n+m}{m-i}-sum_{i=m-n}^{m-1} i imes dbinom{n+m}{m-i-1}\=(n-m)dbinom{n+m}{n}+(m-n)dbinom{n+m}{n}+sum_{i=m-n+1}^m i imes dbinom{n+m}{m-i}-sum_{i=m-n}^{m-1} i imes dbinom{n+m}{m-i-1}\=sum_{i=m-n+1}^{m-1} i imesdbinom{n+m}{m-i}-sum_{i=m-n+1}^{m} (i-1) imes dbinom{n+m}{m-i+1}\=sum_{i=m-n+1}^{m} dbinom{n+m}{m-i}\=sum_{i=0}^{n-1}dbinom{n+m}{i}​$$

    可以发现现在的问题为如何快速求 $F(n,k)=sum_{i=0}^k dbinom{n}{i}$

    可以发现 $F(n,k)=sum_{i=0}^k dbinom{n-1}{i-1}+dbinom{n-1}{i}=2 imes F(n-1,k)-dbinom{n-1}{k}$ 。即 $F(n,k)$ 与 $F(n-1,k)$ 和 $F(n,k-1)$ 都有递推关系。

    直接将答案离线后莫队计算即可。

    时间复杂度 $O((n+m)sqrt{n+m})$ 。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath> 
    #define int long long
    #define mod 1000000007
    using namespace std;
    inline int read(){
        int f=1,ans=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return ans*f;
    }
    const int MAXN=500001;
    struct Query{
        int l,r,bl,id;
    }query[MAXN],tmp[MAXN];
    bool cmp(Query x1,Query x2){
        if(x1.bl!=x2.bl) return x1.l<x2.l;
        if(x1.bl&1) return x1.r<x2.r;
        return x1.r>x2.r;
    }
    int T,p,Ans[MAXN],inv[MAXN],ifac[MAXN],fac[MAXN];
    inline void init(){
        fac[0]=1;for(int i=1;i<MAXN;++i) fac[i]=fac[i-1]*i,fac[i]%=mod;
        inv[1]=1;for(int i=2;i<MAXN;++i) inv[i]=(mod-mod/i)*inv[mod%i],inv[i]%=mod;
        ifac[0]=1;for(int i=1;i<MAXN;++i) ifac[i]=ifac[i-1]*inv[i],ifac[i]%=mod;
        return;
    }
    inline int C(int a,int b){if(a<b) return 0;return (((fac[a]*ifac[b])%mod)*ifac[a-b])%mod;}
    inline int F(int a,int b){return (((fac[b]*fac[a-b])%mod)*ifac[a])%mod;}
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1) ans*=a,ans%=mod;
            a*=a,a%=mod;b>>=1;
        }return ans;
    }
    int blo,bl[MAXN],l,r,ans,inv2;
    int Mod(int x){return ((x%mod)+mod)%mod;}
    signed main(){
    //    freopen("20.in","r",stdin);
        init();
        T=read(),p=read();blo=501;
        for(register int i=0;i<MAXN;++i) bl[i]=(i-1)/blo+1;
        for(register int i=1;i<=T;++i){
            int n=read(),m=read();
            if(n>=m) Ans[i]=(n-m)*C(n+m,n),query[i].l=n+m,query[i].r=m-1,query[i].bl=bl[query[i].l];
            else query[i].l=n+m,query[i].r=n-1,query[i].bl=bl[query[i].l];
            query[i].id=i;
            tmp[i].l=n,tmp[i].r=m;
        }sort(query+1,query+T+1,cmp);
        l=0,r=0,ans=1;inv2=ksm(2,mod-2);
        for(register int i=1;i<=T;++i){
            while(l<query[i].l) ans=Mod(2*ans-C(l,r)),l++;
            while(l>query[i].l) ans=Mod(Mod(ans+C(l-1,r))*inv2),l--;
            while(r<query[i].r) ans+=C(l,r+1),ans=Mod(ans),r++;
            while(r>query[i].r) ans-=C(l,r),ans=Mod(ans),r--;
            Ans[query[i].id]+=ans,Ans[query[i].id]%=mod;
        }
        for(register int i=1;i<=T;++i){Ans[i]*=F(tmp[i].l+tmp[i].r,tmp[i].l);Ans[i]%=mod;}
        for(register int i=1;i<=T;++i) printf("%lld
    ",Ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    var与dynamic
    SQL Server占用服务器内存过高
    SQL SERVER 2012/ 2014 分页,用 OFFSET,FETCH NEXT改写ROW_NUMBER的用法
    SQL Server 2012 OFFSET/FETCH NEXT分页示例
    Solr初始化源码分析-Solr初始化与启动
    (c#) 销毁资源和释放内存
    C#中 ThreadStart和ParameterizedThreadStart区别
    MongoDB的C#封装类
    mongo DB for C#
    C#操作MongoDB
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/11497933.html
Copyright © 2011-2022 走看看