题意:
n(500)*n的矩阵 每行每列只有两个1 现在给出前m行 问 有几种合法的矩阵
思路:
只需要考虑每列上有几个1 然后按行扫描 每次维护行内只有2个1 这样就能构造出合法矩阵
那么我们定义dp[i][j][k]表示扫描到第i行有j列是包含1个1的有k列是包含2个1的 这时500^3数组开不下 于是滚动i
那么对于dp[i][j][k]
如果n-j-k>=2 则可以选择两列分别填1 即转移到dp[i+1][j+2][k]
如果n-j-k>=1&&j>=1 则可以把某个1的列变成2 同时把0的列变成1 即转移到dp[i+1][j][k+1]
如果j>=2 则可以选择两个1变成2 即转移到dp[i+1][j-2][k+2]
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<bitset>
using namespace std;
typedef long long LL;
#define N 510
int n, m, mod;
int dp[2][N][N];
int num[N];
char s[N];
void add(int &x, int y) {
x += y;
if (x >= mod)
x -= mod;
}
int main() {
scanf("%d%d%d", &n, &m, &mod);
for (int i = 1; i <= m; i++) {
scanf("%s", s + 1);
for (int j = 1; j <= n; j++) {
if (s[j] == '1')
num[j]++;
}
}
int j = 0, k = 0;
for (int i = 1; i <= n; i++) {
if (num[i] == 1)
j++;
else if (num[i] == 2)
k++;
}
dp[m & 1][j][k] = 1;
for (int i = m + 1; i <= n; i++) {
memset(dp[i & 1], 0, sizeof(dp[i & 1]));
for (j = 0; j <= n; j++) {
for (k = 0; k + j <= n; k++) {
if (dp[(i & 1) ^ 1][j][k]) {
int zero = n - j - k, one = j, two = k;
LL key = dp[(i & 1) ^ 1][j][k];
if (zero >= 2)
add(dp[i & 1][one + 2][two],
(LL) (zero) * (zero - 1) / 2 % mod * key % mod);
if (zero >= 1 && one >= 1)
add(dp[i & 1][one][two + 1],
key * zero % mod * one % mod);
if (one >= 2)
add(dp[i & 1][one - 2][two + 2],
(LL) (one) * (one - 1) / 2 % mod * key % mod);
}
}
}
}
printf("%d\n", dp[n & 1][0][n]);
return 0;
}