[SCOI2010]生成字符串

首先,将这个字符串抽象成平面直角坐标系,从(0,0)出发,那么每一个1可以看成类似(0,0)->(1,1)这样子走一步,则0就是(0,0)->(1,-1)这样走一步,所以,满足要求的字符串也就是不能走到y=-1的直线上

先撇开这个东西,思考一下满足条件的方案数有多少,直接求不好求,那么可以用总方案数-不满足条件的方案数求得,总方案数其实是很容易求的,我们知道字符串的长度为n+m,一共有n个1,那么只要求在n+m个位置里放置n个1的方案数即可(0就不用管了,放完1剩下的位置放0就好了),也就是求组合数C(n+m,n)

接下来要求的就是不满足条件的方案数,回到这个平面直角坐标系,如果不满足条件,那么一定经过y=-1这一条直线,那么我们将y=-1这条直线上面的翻折下来,见下图:


在这个例子中,我只翻折了一部分(也就这一部分需要翻折,也就是从(0,0)开始直到第一次接触y=-1的这一段),那么所有0操作(向右下走)都变成了1(向右上走),1都变成了0,可以发现,在我圈起来的那一部分之前的部分,0和1的数量持平,所以翻折后他们数量还是一样,但是在我圈起来的地方,0翻成了1,这是只有第一次接触y=-1时才会出现的特殊情况,所以,1的数量其实加了1,0的数量减了1,那么同上,只需要求C(n+m,n+1)就好了

所以,答案就是(C(n+m,n)-C(n+m,n+1))%20100403

但是由于n,m较大,且除法不可以直接取模,所以我们需要运用逆元来求解

逆元:若 a b≡1(mod p) ,则b是a在模p意义下的逆元,也就是(a b)%p≡1

(下面的式子若无特殊标记,则都是在模p意义下的式子)

则 a/b=a * b的逆元

那么转换成乘法后,就可以直接取模了

那么逆元怎么求,请参见费马小定理,大致就是若a与p互质,且p是质数时,a^(p-1)≡1 ,并且,a^-1也是a的逆元

那么有

a^(p-1)=1=a^-1a
a^(p-1)=a^-1a
a^(p-2)=a^-1

所以,a的逆元就是a^(p-2),至于为什么不用a^-1,是因为它可能是个小数

所以组合数(这只是个例子)

          n!
C(n,m)=————————= n! * (m! * (n-m)!)^20100403
       m!(n-m)!

注意其中每一步运算都很大,所以每算一步都要模一次

接下来上代码:

#include <cstdio>
#include <cstring>
#define intt long long
#define mod 20100403

intt n,m;
intt C(intt x,intt y)
{
    intt a=1;
    for(intt i=2;i<=x;i++)//n!
    a=(a*i)%mod;
    intt b=1;
    for(intt i=2;i<=y;i++)//m!
    b=(b*i)%mod;
    intt c=1;
    for(intt i=2;i<=x-y;i++)//(n-m)!
    c=(c*i)%mod;
    b=(b*c)%mod;
    intt d=1;//存(m!*(n-m)!)的逆元 
    intt p=mod-2;
    while(p>0)//快速幂求逆元 
    {
        if(p%2==1)d=(d*b)%mod;
        b=(b*b)%mod;
        p/=2;
    }
    return (a*d)%mod;
}

int main()
{
    scanf("%lld %lld",&n,&m);
    printf("%lld",(C(n+m,n)-C(n+m,n+1)+mod)%mod);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值