AcWing 1317. 树屋阶梯 [题目传送门](https://www.acwing.com/problem/content/description/1319/) ### 一、题目分析 考虑以阶梯左下角那个点为第一个钢材的左下角,那么第一个钢材摆放情况便如下图(以 $n = 5$ 为例)
对每种情况分别讨论,那么问题都被分成了两个子问题,设$f[n]$表示摆放高度为$n$的台阶的方法数,那么: $$\large f[5]=f[4]*f[0]+f[3]*f[1]+f[2]*f[2]+f[1]*f[3]+f[0]*f[4]=\sum_{k=1}^5f(5-k)*f(k-1)$$ 泛化一下,就是: $$\large f[n]=f[n-1]*f[0]+f[n-2]*f[1]+f[n-3]*f[2]+...+f[1]*f[3]+f[0]*f[n-1]=\sum_{k=1}^nf(n-k)*f(k-1)$$ 显然,这就是**卡特兰数**了 但是这题很烦,还要用高精乘: ```c++ #include using namespace std; const int N = 2010; int primes[N], cnt; bool st[N]; int a[N], b[N]; void get_primes(int n) { for (int i = 2; i <= n; i++) { if (!st[i]) primes[cnt++] = i; for (int j = 0; primes[j] * i <= n; j++) { st[i * primes[j]] = true; if (i % primes[j] == 0) break; } } } int get(int n, int p) { // n!中p的次数 int s = 0; while (n) n /= p, s += n; return s; } void mul(int a[], int b, int &len) { int t = 0; for (int i = 1; i <= len; i++) { t += a[i] * b; a[i] = t % 10; t /= 10; } while (t) { a[++len] = t % 10; t /= 10; } //去前导0 while (len > 1 && !a[len]) len--; } int C(int a, int b, int c[]) { int len = 1; c[1] = 1; for (int i = 0; i < cnt; i++) { int p = primes[i]; int s = get(a, p) - get(b, p) - get(a - b, p); while (s--) mul(c, p, len); } return len; } void sub(int a[], int b[], int &len) { int t = 0; for (int i = 1; i <= len; i++) { t = a[i] - b[i] - t; a[i] = (t + 10) % 10; t < 0 ? t = 1 : t = 0; } while (len > 1 && !a[len]) len--; } int main() { //加快读入 ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); get_primes(N - 10); int n; cin >> n; int a1 = C(n + n, n, a); int b1 = C(n + n, n - 1, b); // bl下面没有用到,原因是两数相减,我们知道a>b,按着a的长度来就行了 sub(a, b, a1); for (int i = a1; i >= 1; i--) printf("%d", a[i]); return 0; } ```