Catalan数 费马小定理 快速幂求逆元
题目描述
给定 n n n 个 0 0 0 和 n n n 个 1 1 1,它们将按照某种顺序排成长度为 2 n 2n 2n 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 0 0 0 的个数都不少于 1 1 1 的个数的序列有多少个。答案对 1 0 9 + 7 10^9+7 109+7 取模。
算法思想
将 01 01 01 序列置于坐标系中,起点定于原点。若 0 0 0 表示向右走, 1 1 1 表示向上走,那么任何前缀中 0 0 0 的个数不少于 1 1 1 的个数就转化为,路径上的任意一点,横坐标大于等于纵坐标。题目所求即为这样的合法路径数量。
下图中,表示从 ( 0 , 0 ) (0,0) (0,0) 走到 ( n , n ) (n,n) (n,n) 的路径,在绿线及以下表示合法,若触碰红线即不合法。
在上图中,如果将黑色路径(不合法路径)第一次越过红线位置后面的部分,画一条关于红线轴对称的线,即灰色路径。由图可知,任何一条不合法的路径(如黑色路径),都对应一条从 ( 0 , 0 ) (0,0) (0,0) 走到 ( n − 1 , n + 1 ) (n−1,n+1) (n−1,n+1) 的一条路径(如灰色路径)。而任何一条 ( 0 , 0 ) (0,0) (0,0) 走到 ( n − 1 , n + 1 ) (n−1,n+1) (n−1,n+1) 的路径,也对应了一条从 ( 0 , 0 ) (0,0) (0,0) 走到 ( n , n ) (n,n) (n,n) 的不合法路径。
所以最终答案 a n s = C 2 n n − C 2 n n − 1 = C 2 n n n + 1 ans=C_{2n}^{n}-C_{2n}^{n - 1}=\frac{C_{2n}^{n}}{n + 1} ans=C2nn−C2nn−1=n+1C2nn
费马小定理
如果p是一个质数,而整数 a a a不是 p p p的倍数,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} ≡ 1 (mod p) ap−1≡1(modp)。
本题给出的 p = 1 0 9 + 7 p = 10^9 + 7 p=109+7,为质数,适用费马小定理,即 b b b的乘法逆元为 b p − 2 b^{p - 2} bp−2
快速幂
使用快速幂求乘法逆元。
最终代码
#include <iostream>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
int qmi(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
cin >> n;
//求Catalan(n)
int a = 2 * n, b = n;
int res = 1;
//计算: 2n * (2n - 1) * (2n - 2) * ... * (n + 1) 模mod的结果
for(int i = a; i > a - b; i --)
res = (LL)res * i % mod;
//计算 res * n!的乘法逆元模mod的结果
for(int i = 1; i <= b; i ++)
res = (LL)res * qmi(i, mod - 2, mod) % mod;
//计算 res * (n + 1)的乘法逆元模mod的结果
res = (LL)res * qmi(n + 1, mod - 2, mod) % mod;
cout << res << endl;
return 0;
}