【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\n", (C(n + m, m) - C(n + m, m - 1) + mod) % mod); 24 return 0; 25 }