水过(写这么详细是不是50nb呀!!!)

状态似乎无法压缩,假如压缩需要是一个长位的三进制数,不可取

难道就没法了吗

考虑到问题的特殊性,在确定了前行后,每列只需要放一个或者两个

设目前有列需要填充一个,有列需要填充两个

那么有行可以去填充数字,我们来枚举这行转移

是截至到第行填充后,还剩下列需要放一个,列需要放两个

那么枚举,对当前行做出决策来转移

若本行选择的列都是需要放一个的,那么需要从列选出两列来

若本行选择的列都是需要放一个的,同理

若本行选择的两列,一个在需要放一个的列上,一个在需要放两个的列上

滚动数组来优化空间,然后时间复杂度仍然是

观察到由于每行只需要填充两个

而填充行完之后,只有行需要填充

换句话说,枚举的条件

所以复杂度优化到

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,m,mod,col[504],f[2][504][504],ok[504];
char a[504][504],t,v;
void add(int &u,int v){ u = (u%mod+v%mod)%mod; }
signed main()
{
    scanf("%lld%lld%lld",&n,&m,&mod);
    for(int i=1;i<=m;i++)    scanf("%s",a[i]+1);
    for(int i=1;i<=m;i++)
    for(int j=1;j<=n;j++)
        if( a[i][j]=='1' )    col[j]++;
    for(int i=1;i<=n;i++)    ok[2-col[i]]++;
    f[0][ok[1]][ok[2]] = 1;
    for(int i=1;i<=n-m;i++)
    {
        t = (i&1), v = t^1; int lim = 2*(n-m-i), li = min( ok[2],n-m-i );
        memset( f[t],0,sizeof f[t]);
        for(int q=0;q<=li;q++)//还剩下q个2 
        {
            int j = lim-q*2; if( j>n )    continue;
            f[t][j][q] = 0;
            int qq = (q+2)*(q+1)/2, jj = (j+2)*(j+1)/2;
            add( f[t][j][q], f[v][j][q+1]*j*(q+1) );    //选一个需要填一个的,一个需要填两个的 
            add( f[t][j][q], f[v][j+2][q]*jj );
            if( j>=2 )    add( f[t][j][q], f[v][j-2][q+2]*qq );
        }    
    }
    cout << f[t][0][0];    
}