题目链接

DP

数字从大到小填,小的数字只能填在有大的数的行或者列中。 dp[i][j][k] 表示的状态是填完第i个数后,有i行j列被占用,填下一个数的时候只能填在这些被大的数占用的行或者列当中。

考虑的情况有3种:

  1. 在列的基础上占据一个新的行。
  2. 在行的基础上占据一个新的列。
  3. 在不增加新列和新行的情况下填一个数。
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int dp[85*85][85][85];

int main(void)
{
	int T;
	scanf("%d", &T);
	while(T--) {
		int n, m, mod;
		scanf("%d%d%d", &n, &m, &mod);
		memset(dp, 0, sizeof(dp));
		dp[1][1][1] = n*m;
		for(int i = 2; i <= n*m; ++i) {
			for(int j = 1; j <= n; ++j) {
				for(int k = 1; k <= m; ++k) {
					if(j*k >= i) {
                        if(dp[i-1][j-1][k])
                            dp[i][j][k] = ((long long)dp[i-1][j-1][k]*(n-(j-1))*k)%mod;
                        if(dp[i-1][j][k-1])
                            dp[i][j][k] = (dp[i][j][k]+(long long)dp[i-1][j][k-1]*(m-(k-1))*j)%mod;
                        if(dp[i-1][j][k])
                            dp[i][j][k] = (dp[i][j][k]+(long long)dp[i-1][j][k]*(j*k-i+1))%mod;
					}
				}
			}
		}
		printf("%d\n", dp[n*m][n][m]);
	}
	return 0;
}