【题目描述】
lxhgww最近接到了一个生成字符串的任务,任务需要他把n个1和m个0组成字符串,但是任务还要求在组成的字符串中,在任意的前k个字符中,1的个数不能少于0的个数。现在lxhgww想要知道满足要求的字符串共有多少个,聪明的程序员们,你们能帮助他吗?
【输入】
输入数据是一行,包括2个数字n和m
【输出】
输出数据是一行,包括1个数字,表示满足要求的字符串数目,这个数可能会很大,只需输出这个数除以20100403的余数
【样例输入】
2 2
【样例输出】
2
【数据范围】
对于30%的数据,保证1<=m<=n<=1000
对于100%的数据,保证1<=m<=n<=1000000
此题考察Catalan数的应用以及分数求模运算。
如图所示,证明此问题的解为:
╭ m ╭ m - 1
f(n, m) = │ - │
╰ n + m ╰ n + m
(n - m + 1) (n + m)!
= ───────────
(n + 1)! m!
╭ m
首先,n个1,m个0自由组合,有│ 种。
╰ n + m
接下来可作如下转化:
将每个1对应坐标变换(1, 1),每个0对应坐标变换(1, -1)。
可行的方案为:从(0, 0)通过以上两种坐标变换达到(n + m, n - m),但不能向下越过x轴。
对于不可行的方案总数,如图所示,由于每个不可行的方案必定经过直线y = -1,所以将线路与y = -1最左边的交点向左的部分
沿直线y = -1向下翻折,则不可行的方案总数就变成了:
求从从(0, -2)通过以上两种坐标变换达到(n + m, n - m)的方案总数,即:
╭ m - 1
│ 。
╰ n + m
令MOD = 20100403,则原问题的解为:
(n - m + 1) (n + m)!
ans ≡ ─────────── (mod MOD)
(n + 1)! m!
由分数取模的定义,可得:
(n - m + 1) (n + m)!
m! ans ≡ ─────────── (mod MOD)
(n + 1)!
算出右式,并在[0, MOD)范围内枚举ans,
再输出满足条件的ans的值即可。
另:由费马小定理,可得:
(n - m + 1) (n + m)!
ans ≡ ─────────── ·(m!)^(MOD - 2) (mod MOD)
(n + 1)!
Accode:
#include <fstream>
int main()
{
std::ifstream fin("string.in");
std::ofstream fout("string.out");
typedef long long int64;
const int64 MOD = 20100403;
int64 n, m, ans;
fin >> n >> m;
int64 numer = n - m + 1;
for (int64 i = n + 2; i < n + m + 1; ++i)
(numer *= i) %= MOD;
int64 denom = 1;
for (int64 i = 2; i < m + 1; ++i)
(denom *= i) %= MOD;
for (ans = 0; ans < MOD; ++ans)
if ((denom * ans) % MOD == numer)
break;
fout << ans << std::endl;
fin.close(); fout.close();
return 0;
}