1315. 网格 题解
卡特兰数的几何意义:
在网格图中,从坐标(0,0)走到坐标(n,n)每次向上或者向右走一个单位,且不经过y=x这条线的方案数
如何求卡特兰数
第n项卡特兰数就是以上问题的解
如果不考虑是否越过y=x这条线,从起点到终点,只需要从2n个步骤中选n个向上走,其余向右走,就是方案数
C
2
n
n
C^{n}_{2n}
C2nn
-
画一条y=x+1的线,只要是越过y=x这条线的路径,一定会走到y = x + 1上或者越过这条线,
-
求出(n,n)关于y=x+1的对称点
首先将整个方格向下移动一格,n,n变为(n,n-1),y=x+1变为y=x,
所以(n,n-1)相对于y=x对称的点为(n-1,n)
然后将整个方格向上移动一格复原,得出n,n关于y=x+1对称的点为(n-1,n+1) -
所有经过或越过y=x+1的路径,找到第一次经过y=x+1的点,从这个点开始关于y=x+1
将这条路径对折,一定会走到(n-1,n+1)
由于所有非法路径一定经过y=x+1,所以一定会有一条相应的路径走到(n-1,n+1)
路径条数为:
C
2
n
n
+
1
C^{n+1}_{2n}
C2nn+1
所有方案减去非法方案就是合法方案数量
C
2
n
n
−
C
2
n
n
+
1
C_{2n}^{n} - C_{2n}^{n+1}
C2nn−C2nn+1
=
=
=
1
/
(
n
+
1
)
∗
C
2
n
n
1/(n+1) * C^{n}_{2n}
1/(n+1)∗C2nn
在本题中,求从(0,0)走到(n,m)的方案数,可以类比过来
得到所有方案数为
C
n
+
m
n
C_{n+m}^{n}
Cn+mn
相当于从所有操作中选择n个向右的操作,其余向上,总能走到(n,m)
本题同样要求不能越过y=x这条线
做一条y=x+1的线,对于所有的不合法方案,一定会经过这条线,
使用类似的方法得出(n,m)关于y=x+1对称的点为(m-1,n+1)
所有到达(m-1,n+1)的点的路径对应一个非法方案,方案数为
C
m
+
n
n
+
1
C_{m+n}^{n+1}
Cm+nn+1
所有合法方案数为
C
n
+
m
n
−
C
m
+
n
n
+
1
C_{n+m}^{n}-C_{m+n}^{n+1}
Cn+mn−Cm+nn+1
将
(
m
+
n
)
!
/
(
n
!
)
(
m
!
)
−
(
m
+
n
)
!
/
(
n
+
1
)
!
(
m
−
1
)
!
(m+n)!/(n!)(m!) - (m+n)!/(n+1)!(m-1)!
(m+n)!/(n!)(m!)−(m+n)!/(n+1)!(m−1)!
化简为
(
m
+
n
)
!
(
n
+
1
−
m
)
/
(
n
+
1
)
!
m
!
(m+n)!(n+1-m)/(n+1)!m!
(m+n)!(n+1−m)/(n+1)!m!
使用朴素的高精度+分解质因数求出该式子的结果,就是答案
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 10010;
int prime[N], cnt;
bool st[N];
int n, m;
int a[N], al;
int sum[N];
void init(int n){
for(int i = 2; i <= n; i ++){
if(!st[i]) prime[cnt ++] = i;
for(int j = 0; j < cnt && prime[j] * i <= n; j ++){
st[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
}
int get(int p, int n){
int res = 0;
for(;n;n /= p) res += n / p;
return res;
}
void mul(int a[], int b, int &len){
int r = 0;
for(int i = 0; i < len; i ++){
r += a[i] * b;
a[i] = r % 10;
r /= 10;
}
while(r){
a[len ++] = r % 10;
r /= 10;
}
}
int main(){
init(N - 1);
cin >> n >> m;
for(int i = 0; i < cnt; i ++){
int p = prime[i];
sum[i] = get(p, m + n) - get(p, n + 1) - get(p, m);
}
a[0] = 1;
al = 1;
for(int i = 0; i < cnt; i ++){
int p = prime[i];
int s = sum[i];
while(s --) mul(a, p, al);
}
mul(a, n - m + 1, al);
int t = al;
while(!a[t]) t --;
for(int i = t; i >= 0; i --) cout << a[i] ;
return 0;
}