题目链接
洛谷P1641生成字符串
题目描述
lxhgww 最近接到了一个生成字符串的任务,任务需要他把 n 个 1 和 m 个 0 组成字符串,但是任务还要求在组成的字符串中,在任意的前 k 个字符中,1 的个数不能少于 0 的个数。现在 lxhgww 想要知道满足要求的字符串共有多少个,聪明的程序员们,你们能帮助他吗?
输入格式
输入数据只有一行,包括 2 个数字 n 和 m。
输出格式
输出数据是一行,包括 1 个数字,表示满足要求的字符串数目,这个数可能会很大,只需输出这个数除以 20100403 的余数
数据规模
1 <=m <= n <=10^6
分析:看到前 k 个数 1 的个数不少于 0 的个数,立刻想到那副折线图,想到卡特兰数。
证明过程:
首先,将这个字符串抽象成平面直角坐标系,从(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 时才会出现的特殊情况,在 其他时候走到y=-1 时,0和1的数量一定仍然一样,所以,1的数量其实加了1,0的数量减了1,那么同上,只需要求C(n+m,n+1)就好了
所以,答案就是(C(n+m,n)-C(n+m,n+1))%20100403
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long int ll;
const int N = 2e6+5;
const int mod = 20100403;
int jc[N];
int inv[N];
int n,m;
int power(int a,int n){
int ans = 1;
while(n){
if(n&1) ans = 1ll * ans * a % mod;
a = 1ll * a * a % mod;
n >>= 1;
}
return ans;
}
int C(int n,int m){
int z = 1ll * jc[n] * inv[m] % mod;
return 1ll * z * inv[n-m] % mod;
}
int main(){
scanf("%d%d",&n,&m);
jc[0] = 1;
for(int i = 1;i < N;++i) jc[i] = 1ll * jc[i-1] * i % mod;
inv[N-1] = power(jc[N-1],mod-2);
for(int i = N-2;i >= 0;--i) inv[i] = (1ll * inv[i+1] * (i+1)) % mod;
// cout<<C(n+m,n)<<endl;
// cout<<C(n+m,n-1)<<endl;
printf("%d\n",(C(n+m,m) - C(n+m,m-1) + mod) % mod);
return 0;
}
求组合数过程见:求逆元
参考链接:
https://www.luogu.com.cn/blog/user35379/solution-p1641