BZOJ4806 炮(BZOJ1801、洛谷P2051)

DP

BZOJ4806
BZOJ1801
洛谷P2051

每行每列最多只有两个炮。发现行/列都是相同的,那么记 f [ i ] [ j ] [ k ] 表示前 i 行有 j 列放了一个炮,有 k 列放了两个炮(记0、1、2随便两个都可以)。

考虑如下六种转移情况:

1.当前行不放炮, f [ i ] [ j ] [ k ] = f [ i 1 ] [ j ] [ k ]

2.在没有炮的其中一列放一个炮, f [ i ] [ j ] [ k ] = f [ i 1 ] [ j 1 ] [ k ] ( m ( j 1 ) k )

3.在有一个炮的其中一列放一个炮, f [ i ] [ j ] [ k ] = f [ i 1 ] [ j + 1 ] [ k 1 ] ( j + 1 )

4.在没有炮的两列各放一个炮, f [ i ] [ j ] [ k ] = f [ i 1 ] [ j 2 ] [ k ] C m j k + 2 2

5.在有一个炮的两列各放一个炮, f [ i ] [ j ] [ k ] = f [ i 1 ] [ j + 2 ] [ k 2 ] C j + 2 2

6.在没有炮和有一个炮的两列各放一个炮, f [ i ] [ j ] [ k ] = f [ i 1 ] [ j ] [ k 1 ] ( m j k + 1 ) j

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 105
#define p 9999973
#define C(x) ((x)*(x-1)>>1)
using namespace std;
typedef long long LL;
int n,m;
LL ans,f[N][N][N];
int main(){
    scanf("%d%d",&n,&m);
    f[0][0][0]=1;
    for (int i=1;i<=n;i++)
        for (int j=0;j<=m;j++)
            for (int k=0;k<=m-j;k++){
                (f[i][j][k]+=f[i-1][j][k])%=p;
                if (j) (f[i][j][k]+=f[i-1][j-1][k]*(m-j+1-k)%p)%=p;
                if (j<m&&k) (f[i][j][k]+=f[i-1][j+1][k-1]*(j+1)%p)%=p;
                if (j>1) (f[i][j][k]+=f[i-1][j-2][k]*C(m-j-k+2)%p)%=p;
                if (k>1&&j<m-1) (f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2)%p)%=p;
                if (k) (f[i][j][k]+=f[i-1][j][k-1]*(m-j-k+1)%p*j%p)%=p;
                if (i==n) (ans+=f[i][j][k])%=p;
            }
    return printf("%lld\n",ans),0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/81028216