codeforces 277.5 div2 F:组合计数类dp

题目大意:

求一个 n*n的 (0,1)矩阵,每行每列都只有两个1 的方案数

且该矩阵的前m行已知

分析:

这个题跟牡丹江区域赛的D题有些类似,都是有关矩阵的行列的覆盖问题

牡丹江D是求概率,这个题是方案数,也比较相似。。

这种题中,因为只要求方案数。。我们只要关注几行几列有几个1,而不必要关注具体的位置

题解:

行列都需要处理,因此考虑记录列的状态,然后一行一行的转移

最暴力的方程:

dp[i][j][k][t] 表示已经确定了 i 行 有 j列已经有两个1,有k列只有一个1,有t列一个1也没有的方案数

很显然 第i+1行的两个1 只能放在k列中 或者 t列中

状态转移方程乘上组合数也是很好写的

但是这个方程显然过于暴力。。需要优化

首先,显然 j+k+t=n 因此 t 就不用枚举了

方程变为n3,由于cf服务器很强大,已经可以过了。。

然后又发现 ,前 i 行 应该有且只有 2i 个1 ,所以显然 k=2* i - j

k也不用枚举了。。

最终得到一个二维的dp

代码如下:

 

#include <iostream>

#include <stdio.h>

#include<string.h>

#include<algorithm>

#include<string>

#include<ctype.h>

using namespace std;

#define MAXN 10000

int n,m,mod;

int dp[2][501];

char s[501];

int num[501];

void ini()

{

    memset(num,0,sizeof(num));

    //memset(dp,0,sizeof(dp));

    for(int i=1;i<=m;i++)

    {

        scanf("%s",s);

        for(int i=0;i<n;i++)

        {

            num[i]+=s[i]-'0';

        }

    }

    int j=0;

    for(int i=0;i<n;i++)

    {

        if(num[i]==2)

            j++;

    }

    dp[m%2][j]=1;

}

void solve()

{

    for(int i=m+1;i<=n;i++)

    {

        for(int j=0;j<=n;j++)

        {

            if(!dp[(i-1)%2][j])

                continue;

            int k=2*(i-1)-2*j;

            int t=n-j-k;

            if(k<0)

                break;

            if(j<=n-2&&k>=2)

            {

                dp[i%2][j+2]+=(long long)dp[(i-1)%2][j]*k*(k-1)/2%mod;

                dp[i%2][j+2]%=mod;

            }

            if(t>=2)

            {

                dp[i%2][j]+=(long long)dp[(i-1)%2][j]*(t)*(t-1)/2%mod;

                dp[i%2][j]%=mod;

            }

            if(t&&k)

            {

                dp[i%2][j+1]+=(long long)dp[(i-1)%2][j]*(t)*k%mod;

                dp[i%2][j+1]%=mod;

            }

            dp[(i-1)%2][j]=0;

        }

    }

    printf("%d\n",dp[n%2][n]);

}

int main()

{

    while(scanf("%d%d%d",&n,&m,&mod)!=EOF)

    {

        ini();

        solve();

    }

    return 0;

}

 

你可能感兴趣的:(codeforces)