洛谷P1641 [SCOI2010]生成字符串
? 解题记录 ? ? 洛谷 ? ? 逆元 ? ? 组合数 ? ? 卡特兰数 ?    2017-10-30 23:06:16    359    1    1

题目描述

lxhgww最近接到了一个生成字符串的任务,任务需要他把n个1和m个0组成字符串,但是任务还要求在组成的字符串中,在任意的前k个字符中,1的个数不能少于0的个数。现在lxhgww想要知道满足要求的字符串共有多少个,聪明的程序员们,你们能帮助他吗?

输入输出格式

输入格式:

 

输入数据是一行,包括2个数字n和m

 

输出格式:

 

输出数据是一行,包括1个数字,表示满足要求的字符串数目,这个数可能会很大,只需输出这个数除以20100403的余数

 

输入输出样例

输入样例#1: 复制
2 2
输出样例#1: 复制
2

说明

limitation

每点2秒

对于30%的数据,保证1<=m<=n<=1000

对于100%的数据,保证1<=m<=n<=1000000

来源:SCOI 2010

一道卡特兰数的变式。类似的,受卡特兰数的启发,我们仍然把1看成出栈操作,0看成入栈操作。在不考虑合法性的情况下我们有C(n + m, n) 种方法。又类似的,我们对一个不合法的情况,考虑从前往后第一次取到1比0多1个的时候,我们把前面的1,0取反,这时我们会得到一个有n - 1个1、m + 1个0的序列,而且枚举取反后的序列一定和操作前的序列一一对应。那么不合法序列共有C(n + m, n - 1)种。这个时候我们就可以线性递推组合数做这道题了(PS:快速幂求逆元好写多了呀)。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
int n, m;
LL ans[2];
const LL mod = 20100403;
LL fpow(LL a, LL b, LL p) {
    LL ans = 1;
    while(b) {
        if(b & 1) ans = (ans * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return ans % p;
}
void count(int a, int b) {
    for(register int i = 1; i < b; ++i) {
        ans[0] = ans[1];
        (ans[1] *= (fpow(i + 1, mod - 2, mod) * (a - i)) % mod) %= mod;
    }
}
int main() {
    scanf("%d%d", &n, &m);
    if(n < m) {
        putchar('0');
        return 0;
    }
    const int down = n + m;
    ans[0] = 1, ans[1] = down;
    count(down, m);
    LL res = ans[1] - ans[0] >= 0 ? ans[1] - ans[0] : ans[1] - ans[0] + mod;
    printf("%d", res);
    return 0;
}

 

上一篇: 洛谷P2327 [SCOI2005]扫雷

下一篇: 洛谷P1290 欧几里德的游戏

359 人读过
立即登录, 发表评论.
没有帐号? 立即注册
1 条评论
文档导航