[JZOJ5079] 抛硬币

Description

小A和小B抛硬币,小A抛了A次,小B抛了B次(有顺序)
现在小A想知道,有多少种情况,小A正面朝上的次数比小B多
答案保留十进制下最后K位
10组数据
BAB+10000,K9

Solution

分情况考虑

如果A=B
可以发现小A赢和小B赢的情况数是一样的
平局的次数是 i=1ACiACiB=CAA+B

那么答案就是 2A+BCAA+B2

如果A>B

将两个人抛硬币的结果用二进制状态表示,1表示正面朝上

如果小A没赢,那么将这个状态二进制翻转,得到的结果一定是小A赢的
所以我们只需要求出一个值S,表示有多少种情况是小A赢,且翻转过来还是小A赢
答案就是 2A+B+S2

设小A抛正面朝上x次,小B正面朝上y次
那么 x>y Ax>By
化一下就是 0<xy<AB
同时x<=A,y<=B

设d=x-y
那么

S=d=1AB1y=0BCy+dACyB

=d=1AB1y=0BCy+dACByB

根据组合恒等式,可以化成下面

=d=1AB1CB+dA+B

这个就随便做了,剩下的就是组合数取模的问题了

Code

惨遭卡常
可能是我组合数取模写丑了?

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
#define N 10000001
#define M 1000001
using namespace std;
LL mo,cf[11],lim,m1,m2,ny1,ny2;
int js2[N],js5[N],num2[M],num5[M],sum2[M],sum5[M];
LL kst(LL x,LL y,LL mo)
{
    if(!x||!y) return 0;
    if(y==1) return x;
    if(x<=1e9&&y<=1e9) return x*y%mo;
    LL s=kst(x,y/2,mo);
    return(y&1)?(s+s+x)%mo:(s+s)%mo;
}
LL ksm(LL k,LL n,LL mo)
{
    if(k==0) return 0;
    if(k==1) return 1;
    k%=mo;
    LL s=1;
    for(;n;n>>=1,k=kst(k,k,mo)) 
    {
        if(k==1) return s;
        if(n&1) s=kst(s,k,mo)%mo;
    }
    return s;
}
LL ks(LL k,LL n,LL mo)
{
    if(k==0) return 0;
    if(k==1) return 1;
    k%=mo;
    LL s=1;
    for(;n;n>>=1,k=k*k%mo) 
    {
        if(k==1) return s;
        if(n&1) s=s*k%mo;
    }
    return s;
}
LL js(LL n,LL p,LL &v,LL mo)
{
    if(n<=M-1) 
    {
        if(p==2) 
        {
            v=num2[n];
            return sum2[n];
        }
        else 
        {
            v=num5[n];
            return sum5[n];
        }
    }
    if(n==0) 
    {
        v=0;
        return 1;
    }
    LL s1,v1;
    s1=js(n/p,p,v1,mo);
    v=v1+n/p;
    if(p==2) return s1*ks(js2[mo]%mo,n/mo,mo)%mo*(js2[n%mo]%mo)%mo;
    else return s1*ks(js5[mo]%mo,n/mo,mo)%mo*(js5[n%mo]%mo)%mo;
}
void exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0) x=1,y=0;
    else
    {
        exgcd(b,a%b,x,y);
        LL p=x;
        x=y,y=p-(a/b)*y;
    }
}
LL ny(LL k,LL mo)
{
    LL a=k,b=mo,x,y;
    exgcd(a,b,x,y);
    return (x+mo)%mo;
}
LL doit(LL n,LL m,LL pr,LL mo)
{
    LL s1,p,s2,q,s3,v;
    s1=js(n,pr,p,mo);
    s2=js(m,pr,q,mo);
    s3=js(n-m,pr,v,mo);
    p-=q+v;
    if(p>=lim) return 0;    
    return s1*ny(s2,mo)%mo*ny(s3,mo)%mo*ks(pr,p,mo)%mo;
}
LL C(LL m,LL n)
{   
    if(m>n) return 0;
    LL sx=doit(n,m,2,m1),sy=doit(n,m,5,m2);
    return (sx*m2%mo*ny1%mo+sy*m1%mo*ny2%mo)%mo;
}
LL sum(LL a,LL b)
{
    LL s=0;
    fo(i,1,a-b-1) (s+=C(b+i,a+b))%=mo;
    return s;
}
int main()
{
    cf[0]=1;
    fo(i,1,10) cf[i]=cf[i-1]*(LL)10;
    LL a,b;
     m1=ksm(2,10,cf[10]),m2=ksm(5,10,cf[10]);
    js2[0]=js5[0]=1;
    fo(i,1,N-1) 
    {
        js2[i]=js2[i-1],js5[i]=js5[i-1];
        if(i%2!=0) js2[i]=(LL)js2[i]*(LL)i%m1;
        if(i%5!=0) js5[i]=(LL)js5[i]*(LL)i%m2;
    }
    sum2[0]=sum5[0]=1;
    fo(i,1,M-1)
    {
        num2[i]=num2[i-1],num5[i]=num5[i-1],sum2[i]=sum2[i-1],sum5[i]=sum5[i-1];
        LL i1=i;
        while(i1%2==0) num2[i]++,i1>>=1;
        sum2[i]=sum2[i]*i1%m1;
        i1=i;
        while(i1%5==0) num5[i]++,i1/=5;
        sum5[i]=sum5[i]*i1%m2;
    }
    while(scanf("%lld%lld%d",&a,&b,&lim)!=EOF)
    {
        mo=cf[lim];
        mo=mo*(LL)10;
        lim++;
        m1=ksm(2,lim,mo),m2=ksm(5,lim,mo);
        ny1=ny(m2,m1),ny2=ny(m1,m2);
        LL ans;
        if(a==b) ans=(ksm(2,a+b,mo)-C(a,a+b)+mo)%mo/2;
        else ans=(ksm(2,a+b,mo)+sum(a,b))%mo/2;
        ans=ans%(mo/10);
        fod(i,lim-2,1) if(ans<cf[i]) printf("0");
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值