详解卡特兰数经典应用

题目链接
洛谷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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值