版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/88976379
Problem
https://jzoj.net/senior/#main/show/4779
给定一个 的矩阵 , ,定义合法点为这一行这一列中严格最大的点。求矩阵至少有一个合法点的数目。
Solution
首先肯定是考虑容斥了。方案数 = 至少有一个合法点数目 - 至少有两个 +至少有三个 ……
我们考虑记 表示当前的矩阵最大值小于等于 ,至少有 个合法点的数目。
转移必然是枚举一个值为 的合法点数目了。然后乘上一个组合数进行转移。为了不重不漏,可以把前面 个合法点看成一个整体,那么就剩下 行, 列,选出其中 种组合放合法点,由于这 行列顺序随意,所以方案数就是 当然,因为这 行 列中只放了 个点,其余点都可以放 中任意一个数,所以要再配上一个形如 的转移系数。
最后对于每个 ,还要乘上一个形如 的系数,因为有些格还没放完。
注意转移 时的意义,实际上是做一个前缀和,因为 的含义是最大值小于等于 ,并非一定要有 的值。我一开始傻到直接又做一遍前缀和,才发现可以直接 转移。。
Code
#include <bits/stdc++.h>
#define F(i, a, b) for (LL i = a; i <= b; i ++)
typedef long long LL;
const LL N = 2e3 + 10, K = 11;
using namespace std;
LL n, m, k, l, ans;
LL f[N][K], C[N][N], jc[N], KSM[K][2 * N * N];
int main() {
scanf("%d%d%d%d", &n, &m, &k, &l);
jc[0] = 1;
F(i, 0, max(n, m) + 1)
C[i][0] = 1;
F(i, 1, max(n, m) + 1)
F(j, 1, max(n, m) + 1)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % l;
F(i, 1, max(n, m))
jc[i] = 1ll * jc[i - 1] * i % l;
F(j, 1, k) {
KSM[j][0] = 1;
F(i, 1, 2 * n * m)
KSM[j][i] = KSM[j][i - 1] * j % l;
}
f[0][1] = 1; int w = min(n, m);
F(i, 0, w)
F(j, 1, k - 1)
if (f[i][j])
F(t, 0, w - i)
if (n - i >= t && m - i >= t)
f[i + t][j + 1] = (f[i + t][j + 1] + 1ll * f[i][j] * C[n - i][t] % l * C[m - i][t] % l * jc[t] % l * KSM[j][(n + m - 2 * i - t - 1) * t] % l) % l;
F(i, 1, w)
ans = (ans + ((i & 1) ? (1) : (- 1)) * f[i][k] * KSM[k][(n - i) * (m - i)] % l) % l;
printf("%d\n", (ans + l) % l);
}