要求在n*m矩阵中填入1-n*m,使得存在且仅存在1个位置,这个位置的数在所在的行与列都是最大值。问有多少种方案。
易知这个位置上必然是n*m。为了满足有且仅有一个点符合条件,我们从n*m倒着往矩阵里面放,显然每个数都应该放在已经存在数的行或列上。
考虑用i个数已经在j行k列上放置了数字,在放第i+1个数之后只可能覆盖j行k列、j+1行k列、j行k+1列。这样分别有j*k-i、k*(n-j)、(m-k)*j种方法。依此进行状态转移,答案即为dp[n*m][n][m]。
第一次惊险的卡到了4882ms……后面优化到了3307ms
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 82;
const int INF = 0x3f3f3f3f;
int t, n, m;
ll mod, dp[maxn*maxn][maxn][maxn];
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%d%lld", &n, &m, &mod);
memset(dp, 0, sizeof(dp));
dp[1][1][1] = n*m;
for(int i = 1;i < n*m;i++)
{
for(int j = 1;j <= n;j++)
{
if(i < j) break;
for(int k = 1;k <= m;k++)
{
if(i < k) break;
if(!dp[i][j][k]) continue;
if(k < m)
dp[i+1][j][k+1] = (dp[i+1][j][k+1] + dp[i][j][k]*j*(m - k)) % mod;
if(j < n)
dp[i+1][j+1][k] = (dp[i+1][j+1][k] + dp[i][j][k]*(n - j)*k) % mod;
if(i < j*k)
dp[i+1][j][k] = (dp[i+1][j][k] + dp[i][j][k]*(j*k - i)) % mod;
}
}
}
printf("%lld\n", dp[n*m][n][m]);
}
return 0;
}