版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/Gx_Man_VIP/article/details/88647155
题目大意:
分析:
令
表示矩阵中至少存在
个鞍点,且这些点的数值≤
时矩阵的方案数。
考虑如何转移:
假设我们当前的
而言,我再加入
个数值为
的鞍点,
那么这些鞍点能够放的位置的方案数即:
然后它们所占用的行列的其他格子显然只能取
的数值,即有
种方案
最后统计答案的时候呢,
注意到
中除去这
个我人为放下的鞍点以外,还有
个位置未放置且能够任意放置
的数值,那么
乘上
答案要容斥后即为
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cmath>
#include <algorithm>
#define N 2005
using namespace std;
typedef long long ll;
ll dp[N][N], mi[11][N*N], c[N][N], jc[N], mo, ans;
int n, m, K, num, x;
int main()
{
scanf("%d %d %lld %lld", &n, &m, &K, &mo);
c[0][0] = 1;
for (int i = 1; i <= 2000; i++)
{
c[i][0] = 1;
for (int j = 1; j <= i; j++) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
}
for (int j = 0; j <= K; j++)
{
mi[j][0] = 1;
for (int i = 1; i <= n * m; i++)
mi[j][i] = (ll)mi[j][i - 1] * j % mo;
}
num = min(n, m);
jc[0] = 1; for (int i = 1; i <= num; i++) jc[i] = (ll)jc[i - 1] * i % mo;
dp[0][0] = 1;
for (int i = 0; i < K; i++)
for (int j = 0; j <= num; j++)
if (dp[i][j])
for (int k = 0; k <= num - j; k++)
dp[i + 1][j + k]= (dp[i + 1][j + k] + dp[i][j] * c[n - j][k] % mo * c[m - j][k] % mo * jc[k] % mo * mi[i][(m - j) * k + (n - j) * k - k * k - k] % mo) % mo;
int j;
for (int i = 1; i <= num; i++)
{
if (i % 2 == 1) j = 1; else j = -1;
ans = ((ans + dp[K][i] * mi[K][(m - i) * (n - i)] % mo * j) % mo + mo) % mo;
}
printf("%lld\n", ans);
}