Codeforces Round #277.5 (Div. 2)F题

An n × n square matrix is special, if:

  • it is binary, that is, each cell contains either a 0, or a 1;
  • the number of ones in each row and column equals 2.

You are given n and the first m rows of the matrix. Print the number of special n × n matrices, such that the first m rows coincide with the given ones.

As the required value can be rather large, print the remainder after dividing the value by the given number mod.

Input

The first line of the input contains three integers nmmod (2 ≤ n ≤ 5000 ≤ m ≤ n2 ≤ mod ≤ 109). Then m lines follow, each of them contains n characters — the first rows of the required special matrices. Each of these lines contains exactly two characters '1', the rest characters are '0'. Each column of the given m × n table contains at most two numbers one.

Output

Print the remainder after dividing the required value by number mod.

Sample test(s)
input1
3 1 1000
011
output1
2
input2
4 4 100500
0110
1010
1010
output2
1
题目大意:要求一个满足条件的方阵n*n(只有0,1),满足每一行每一列有且只有两个1。先给出方阵前面的m行,和取余的mod。求有多少这样的方阵。
题目分析:因为n<=500,用搜索的话一定超时,所以想DP,至于怎么DP就得好好想想状态,如果想着要记录前面行的状态的话肯定是记不下来的。所以要换角度想,我们直接储存第i列有0,1个1。然后我们只要在后面的行里面选这些里面的列来填1(用排列组合就可以了)。但是如果你写一下就会发现状态并不是简单的递减或递增的关系,所以要转换成记忆化搜索。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{
    int x,y;
}dl[600000];
int l,r,a[510],c[510][510],q[510][510];///f[i,j]i个0空位,j个1空位。
long long mod,f[510][510];
void ditui(int k)
{
    q[dl[k].x][dl[k].y]=0;
    if (dl[k].x>0&&dl[k].y>0) {
        if (!q[dl[k].x][dl[k].y-1]) {
            r++;dl[r].x=dl[k].x;dl[r].y=dl[k].y-1;
            f[dl[k].x][dl[k].y-1]=(f[dl[k].x][dl[k].y]*dl[k].x*dl[k].y)% mod;
            q[dl[k].x][dl[k].y-1]=1;
        } else {
            f[dl[k].x][dl[k].y-1]=(f[dl[k].x][dl[k].y-1]+f[dl[k].x][dl[k].y]*dl[k].x*dl[k].y)% mod;
        }
    }
    if (dl[k].x>1) {
        if (!q[dl[k].x-2][dl[k].y]) {
            r++;dl[r].x=dl[k].x-2;dl[r].y=dl[k].y;
            f[dl[k].x-2][dl[k].y]=(f[dl[k].x][dl[k].y]*c[dl[k].x][2])% mod;
            q[dl[k].x-2][dl[k].y]=1;
        } else {
            f[dl[k].x-2][dl[k].y]=(f[dl[k].x-2][dl[k].y]+f[dl[k].x][dl[k].y]*c[dl[k].x][2])% mod;
        }
    }
    if (dl[k].y>1) {
        if (!q[dl[k].x+2][dl[k].y-2]) {
            r++;dl[r].x=dl[k].x+2;dl[r].y=dl[k].y-2;
            f[dl[k].x+2][dl[k].y-2]=(f[dl[k].x][dl[k].y]*c[dl[k].y][2])% mod;
            q[dl[k].x+2][dl[k].y-2]=1;
        } else {
            f[dl[k].x+2][dl[k].y-2]=(f[dl[k].x+2][dl[k].y-2]+f[dl[k].x][dl[k].y]*c[dl[k].y][2])% mod;
        }
    }
}
int main()
{
    int i,j,k,n,m,one,zero;
    char ch;
    scanf("%d%d%I64d", &n, &m, &mod);
    memset(a,0,sizeof(a));
    memset(q,0,sizeof(q));
    memset(f,0,sizeof(f));
    getchar();
    for (i=1;i<=m;i++){
        for (j=1;j<=n;j++) {
            scanf("%c", &ch);
            if (ch=='1') a[j]++;
        }
        getchar();
    }
    one=0;zero=0;
    for (i=1;i<=n;i++)
    if (a[i]==0) zero++;
    else if (a[i]==1) one++;
    for (i=0;i<=n;i++) {
        c[i][0]=1;c[i][i]=1;
        for (j=1;j<i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])% mod;
    }
    l=1;r=1;
    f[one][zero]=1;q[one][zero]=1;
    dl[1].x=one;dl[1].y=zero;
    while (l<=r) {
        ditui(l);l++;
    }
    /*递推过程会出现问题,就是推出后面的,
    原来的0位置放一个就会变成1,会改变前面的状态!
    f[one][zero]=1;q[one][zero]=1;///2 1
    for (i=n;i>=0;i--)
        for (j=n;j>=0;j--) {
        if (q[i+1][j+1]) {
            f[i+1][j]=(f[i+1][j]+f[i+1][j+1]*(i+1)*(j+1))% mod;
            q[i+1][j]=1;
            }
        if (q[i][j+2]) {
            f[i+2][j]=(f[i+2][j]+f[i][j+2]*c[j+2][2])% mod;
            q[i+2][j]=1;
            }
        if (q[i+2][j]) {
            f[i][j]=(f[i][j]+f[i+2][j]*c[i+2][2])% mod;
            q[i][j]=1;
            }
        }
    */
    printf("%I64d\n", f[0][0]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值