题意:
有一种由[1, n*m]不重复填充的矩阵,并且只有一个点在行和列上是最大的。求这种 n*m 矩阵有多少种。
题解:
最大值一定是 n*m,我们可以把他放在任意格子,但是我们可以发现,无论放在哪里他的矩阵都可以变形会最大值在(1,1)的情况。
9 | 8 | 7 |
6 | 5 | 4 |
3 | 2 | 1 |
1 | 3 | 2 | |
7 | 9 | 8 | 7 |
4 | 6 | 5 | 4 |
3 | 2 | 1 |
所以我们计算出最大值在(1,1)的情况在乘上 n*m。
我们用 dp[k][i][j] 来保存矩阵个数(i 表示可以用的行数,j 表示可以用的列数,k 表示已经填好的数字)
假设最大值是 9:
9 在(1,1)接下来就是放 8 ,现在我们可以知道在第一行和第一列可以放(因为最大值是 9 ,可以压制 8),所以如果把 8 放在第一行就有 m-1 个格子可以放,即 (m-1)*dp[1][1][1];如果把 8 放在第一列就有 n-1 个格子可以放,即 (n-1)*dp[1][1][1];这样我们就得到 dp[2][2][1] = 2, dp[2][1][2] = 2。
接下来就放7,现在由上一步可以知道我们可以知道有 2 行 2 列可以放,像上一步可以通过放在外围多加一行和多加一列,即:dp[3][3][1] = (m-2)*dp[2][2][1] = 2;dp[3][1][3] = (m-2)*dp[2][1][2] = 2。由于上次我们填 8 时可以用的 2 行 2 列也是可以填 7 的,条件就是 i * j > k-1 未填满矩阵,所以 dp[3][2][2] = (i * j - k + 1) * dp[2][2][2] + (m - j + 1 ) * i * dp[2][2][1] + (n - i + 1) * j * dp[2][1][2]= 16(dp[2][2][2] 初始化的时候是 0 ,因为放 8 根本不可以放在(2,2))。
所以 dp 公式就是:
当 i * j > k - 1时,dp[k][i][j] = (i * j- k + 1) * dp[k+1][i][j] + (m - j + 1) * i * dp[k-1][i][j-1] + (n - i + 1) * j * dp[k][i-1][j]。
AC代码:
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <bitset>
#include <stack>
#include <cmath>
#include <deque>
#include <queue>
#include <list>
#include <set>
#include <map>
#define mem(a, b) memset(a, b, sizeof(a))
#define pi acos(-1)
using namespace std;
typedef long long ll;
ll n, m, mod;
ll dp[6410][85][85];
ll work(){
for(int k = 2; k <= n*m; k++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(i*j > k-1){
dp[k][i][j] = dp[k-1][i-1][j]*(n-i+1)*j % mod;
dp[k][i][j] = (dp[k][i][j]+dp[k-1][i][j-1]*(m-j+1)*i%mod) % mod;
dp[k][i][j] = (dp[k][i][j]+dp[k-1][i][j]*(i*j-k+1)%mod) % mod;
}
}
}
}
return dp[n*m][n][m];
}
int main(){
int t;
scanf("%d", &t);
while(t--){
scanf("%lld %lld %lld", &n, &m, &mod);
dp[1][1][1] = 1;
printf("%lld\n", work()*n*m%mod);
}
}