【SCOI2010】生成字符串

Description

给定n,m,求一个包含n个1,m个0,且任何前缀的1的数量必须大于0的数量的合法01串的数量,答案对20100403取模。

Solution

我们建立坐标系,定义x坐标为1和0的数量的和,y坐标表示1和0的数量的差,那么向右上走就表示选择1,向右下走就表示选择0。

如果不考虑限制条件,那么我们要从(0,0)走到(n+m,n-m),就相当于从n+m步中选出m步向右下走,方案数就是$C_{n+m}^m$

现在考虑限制条件:任何前缀的1的数量必须大于0的数量,那么就意味着我们的在坐标系中的路径不能经过直线y=-1,那么非法的方案就是从(0,0)走到(x,-1)再走到(n+m,n-m),这就等价于从(0,-2)开始走到(n+m,n-m),即方案数为$C_{n+m}^{m-1}$,那么合法方案数就是这两个方案数之差。

我们首先预处理出一个阶乘数组和一个阶乘的逆元数组,逆元可以通过费马小定理或者是线性递推实现。

然后套用组合数的公式即可完成。

Code

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 ll n, m, fac[2000010], inv[2000010];
 5 const ll mod = 20100403;
 6 inline ll qpow(ll a, ll n) {
 7     ll ret = 1;
 8     while (n) {
 9         if (n & 1) ret = (ret * a) % mod;
10         a = a * a % mod;
11         n >>= 1;
12     }
13     return ret;
14 }
15 inline ll C(ll n, ll m) {
16     return ((fac[n] * inv[m]) % mod) * inv[n - m] % mod;
17 }
18 int main() {
19     scanf("%lld%lld", &n, &m);
20     fac[1] = 1;
21     for (register int i = 2; i <= n + m; ++i) fac[i] = fac[i - 1] * i % mod;
22     for (register int i = 1; i <= n + m; ++i) inv[i] = qpow(fac[i], mod - 2);
23     printf("%lld
", (C(n + m, m) - C(n + m, m - 1) + mod) % mod);
24     return 0;
25 }
AC Code
原文地址:https://www.cnblogs.com/shl-blog/p/11295818.html