「JLOI2015」骗我呢

由题意,每一行内的数单调递增。又因为 \(0 \leqslant a_{i,j} \leqslant m\) 限制了这些数的取值范围。

那么我们相当于在 \(m + 1\) 个数中选 \(m\) 个数。必然有两个之间相差 \(2\),其余的数连续。

我们设 \(f_{i,j}\) 表示第 \(i\) 行中,被舍弃掉的数是 \(j\) 的方案数。

那么则有

\[\begin{aligned} f_{i,j} &= \sum_{k=0}^{j+1} f_{i-1,k} \\ &=\sum_{k=0}^{j} f_{i-1,k} + f_{i-1,j+1} \\ &= f_{i,j-1} + f_{i-1,j+1} \end{aligned} \]

这个式子看上去是一个表格中的递推,我们将其画到网格图中(第 \(i\) 行第 \(j\) 列):

「JLOI2015」骗我呢

这个样子很丑,我们尽量将转移变成水平/垂直的。

「JLOI2015」骗我呢

虚线为原本的转移,将其改成向上再向右。

这样就有一点像计数问题了。

我们在两侧加上两条直线:

「JLOI2015」骗我呢

不难发现,结果即为从 \((0,0)\) 到 \((n+m,n)\) ,不经过直线 \(A:y=x+1\) 和 \(B:y=x-m-2\) 的路径总数。

考虑用组合数算这个东西。我们定义由 \(A\) 和 \(B\) 组成的序列表示先经过 \(A\) 或 \(B\) 的方案。如 \(AABABBAB\)。

我们发现,连续经过同一条直线没有影响,所以把所有的相同部分全部缩成一个。

显然,它要么以 \(A\) 开头,要么以 \(B\) 开头。

我们直接用总方案数 ( \(\tbinom{2n+m+1}{n}\) ) - \(A\) 开头的方案数 - \(B\) 开头的方案数。

但是后两者又该怎么计算呢?

我们使用对称。

我们将直线 \(B\) 关于直线 \(A\) 对称过去。过 \(A\) 再过 \(B\) 实质上就是不停的跨越直线的过程。我们一直对称下去。当我们发现,对称过去的某个点在回来之后并不属于第一象限了,说明这条路径不合法。

容斥。\(A\) 开头相当于 \(A+AB-BA-BAB+ABA+ABAB \cdots\)。 \(B\) 开头同理。

我们每次对称都必然会使得距离加一,所以最终一定能找到答案。

#include <cstdio>
const int ccf = 1e9 + 7, N = 3e6 + 5;
int n, m, nn, fac[N], inv[N];
void init() {
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    nn = n * 2 + m + 1;
    for (int i = 2; i <= nn; i++) fac[i] = 1ll * fac[i - 1] * i % ccf;
    for (int i = 2; i <= nn; i++) inv[i] = 1ll * (ccf - ccf / i) * inv[ccf % i] % ccf;
    for (int i = 2; i <= nn; i++) inv[i] = 1ll * inv[i] * inv[i - 1] % ccf;
}
int C(int n, int m) { return 1ll * fac[n] * inv[m] % ccf * inv[n - m] % ccf; }
int Mir(int x, int y, int dir) {
    if (x < 0 || y < 0) return 0;
    int delta = dir ? -m - 2 : 1;
    return (C(x + y, y) - Mir(y + delta, x - delta, dir ^ 1) + ccf) % ccf;
}
int main(void) {
    scanf("%d%d", &n, &m);
    init();
    int ans = C(nn, n);
    ans = (ans - Mir(n - 1, n + m + 2, 0) + ccf) % ccf;
    ans = (ans - Mir(n + m + 2, n - 1, 1) + ccf) % ccf;
    printf("%d\n", ans);
    return 0;
}
上一篇:【洛谷2480】[SDOI2010] 古代猪文(卢卡斯定理+中国剩余定理)


下一篇:[CF1342E] Placing Rooks - 第二类斯特林数