原题链接
题意
- 给定一个 (n imes m) 的棋盘
- 给定每行一个左区间 ([1, L[i]]) 和一个右区间 ([m - R[i] + 1, m])
- 每列有且仅能有一颗棋子, 每个左区间有且仅有一颗棋子, 右区间有且仅有一颗棋子
- 保证每一行的左右区间不相交
求合法方案数
解题报告
这题挺妙的, 虽然可能妙得不是非常直观
首先, 因为考虑每一列只能填一个, 考虑怎么填
声明 (l[i]) 为以 (i) 为左区间右端点的行数, (r[i]) 为以 (i) 为右区间左端点的行数, (mid[i]) 为第 (i) 列上有多少没有被左右区间覆盖的行数
于是可以设计一个状态 (f[i][j][k]) 表示当前考虑到了第 (i) 列, 有 (j) 列没填, (k) 行左端点 (le i) 的右区间没有填任何棋子的方案数
则能够考虑第 (i + 1) 列的棋子填在哪里, 而且每次让左区间强行满足条件, 即左区间右端点 (le i) 的每一个左区间都有且仅有一颗棋子
有:
第 (i + 1) 列的棋子被左区间覆盖:
[f[i + 1][j - l[i + 1] + 1][k + r[i + 1]] += f[i][j][k] imes A_{j + 1}^{l[i + 1]}
]
第 (i + 1) 列的棋子被右区间覆盖:
[f[i + 1][j - l[i + 1]][k + r[i + 1] - 1] += f[i][j][k] imes A_{j}^{l[i + 1]} imes (k + r[i + 1])
]
第 (i + 1) 列的棋子没有被左区间或右区间覆盖:
[f[i + 1][j - l[i + 1]][k + r[i + 1]] += f[i][j][k] imes A_{j}^{l[i + 1]} imes (mid[i + 1])
]
然后最后答案就是 $$sumlimits_{i = 0}^{m} f[m][i][0]$$
代码实现
Sample Code
#include <cstdio>
#define re register
#define rep(i, a, b) for(re int i = (a); i <= (b); ++ i)
#define Rep(i, a, b) for(re int i = (a); i < (b); ++ i)
typedef long long ll;
const ll mod = 1e9 + 7;
const ll N = 2e2 + 5;
int n, m, l, r;
ll MID[N], L[N], R[N];
ll ans, f[N][N][N];
ll fac[N], P[N][N];
void add(ll &x, ll y) { x = (x + y) % mod; }
int main() {
scanf("%d%d", &n, &m);
rep(i, 1, n) {
scanf("%d%d", &l, &r);
rep(j, l + 1, m - r)
MID[j] ++;
L[l] ++, R[m - r + 1] ++;
}
rep(i, 0, m) P[i][0] = 1;
rep(i, 1, m) rep(j, 1, i)
P[i][j] = (P[i - 1][j - 1] + P[i - 1][j]) % mod;
fac[0] = 1;
rep(i, 1, m) fac[i] = fac[i - 1] * i % mod;
rep(i, 1, m) rep(j, 1, i)
P[i][j] = P[i][j] * fac[j] % mod;
f[0][0][0] = 1;
Rep(i, 0, m) rep(j, 0, i) rep(k, 0, n) {
if (!f[i][j][k]) continue;
if (j + 1 >= L[i + 1]) {
ll tmp = f[i][j][k] * P[j + 1][L[i + 1]] % mod;
add(f[i + 1][j + 1 - L[i + 1]][k + R[i + 1]], tmp);
}
if(j >= L[i + 1]) {
ll tmp = f[i][j][k] * MID[i + 1] % mod * P[j][L[i + 1]] % mod;
add(f[i + 1][j - L[i + 1]][k + R[i + 1]], tmp);
if(k + R[i + 1]) {
tmp = f[i][j][k] * (k + R[i + 1]) % mod * P[j][L[i + 1]] % mod;
add(f[i + 1][j - L[i + 1]][k + R[i + 1] - 1], tmp);
}
}
} rep(i, 0, m) add(ans, f[m][i][0]);
printf("%lld
", ans);
return 0;
}