传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2822

这道题做得比较纠结,开始并没有看出什么,于是傻乎乎的写了个O(n^3)的dp,设f[i][j]表示前i行,第i行有j条竖线的方案数,

则f[i][j] = sigma(f[i - 1][k]) (j - 1 <= k < i - 1)

经打表发现这其实是卡特兰数列,吓傻了⊙﹏⊙b汗

其实我们可以发现,只需要枚举一个分割的矩形,就可以把原图分为两个部分,设f[i]表示有i行的方案数,则f[i] = sigma(f[k] * f[i - k - 1]) (0 <= k < i)

这就是卡特兰数列,而为了防止超时,将式子变形为:f[i] = f[i - 1] * (4 * i - 2) / (i + 1);

复杂度为O(n) * 高精度

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 510;
struct node {
    int a[maxn];
    node() {
        memset(a, 0, sizeof(a));
        a[0] = 1;
    }
    void operator *= (int t) {
        for(int i = 1; i <= a[0]; i ++) {
            a[i] *= t;
        }
        for(int i = 1; i <= a[0]; i ++) {
            a[i + 1] += a[i] / 10;
            a[i] %= 10;
        }
        int k = a[0] + 1;
        while(a[k]) {
            a[k + 1] += a[k] / 10;
            a[k] %= 10;
            k ++;
        }
        k --;
        a[0] = k;
    }
    node operator / (int t) {
        node tt;
        int res = 0;
        for(int i = a[0]; i ; i --) {
            res = res * 10 + a[i];
            tt.a[i] = res / t;
            res %= t;
        }
        int k = a[0];
        while(k > 1 && !tt.a[k]) k --;
        tt.a[0] = k;
        return tt;
    }
    void print() {
        for(int i = a[0]; i ; i --) {
            printf("%d", a[i]);
        }
        printf("\n");
    }
}f[maxn];
int n;
int main() {
    scanf("%d", &n);
    f[0].a[1] = f[1].a[1] = 1;
    for(int i = 2; i <= n; i ++) {
        f[i] = f[i - 1];
        int t1 = 4 * i - 2, t2 = i + 1;
        f[i] *= t1;
        f[i] = f[i] / t2;
    }
    f[n].print();
    return 0;
}