因為我們并不關心具體到每一列、每一行的那些棋子是如何分布的,只關注相應的行和列有多少個棋子,所以我們設置狀態 dp[ i ][ j ][ k ][ t ],意義為到了第 i 行時,有 j 列沒有放置棋子,有 k 列放置了一個棋子,有 t 列放置了兩個棋子,又因為 j + k + t 恒等于 m,所以可以省略掉最后一維
轉移的話分為三大種情況,六個小情況分別討論:
第 i 行不放棋子:dp[ i ][ j ][ k ] += dp[ i - 1 ][ j ][ k ]
第 i 行放一個棋子:
放在空列上:dp[ i ][ j ][ k ] += dp[ i - 1 ][ j + 1 ][ k - 1 ] * ( j + 1 )
放在有一個棋子的列上:dp[ i ][ j ][ k ] += dp[ i - 1 ][ j ][ k + 1 ] * ( k + 1 )
第 i 行放兩個棋子:
都放在空列上:dp[ i ][ j ][ k ] += dp[ i - 1 ][ j + 2 ][ k - 2 ] * C( j + 2 )
都放在有一個棋子的列上:dp[ i ][ j ][ k ] += dp[ i - 1 ][ j ][ k + 2 ] * C( k + 2 )
各放一個:dp[ i ][ j ][ k ] += dp[ i - 1 ][ j + 1 ][ k ] * ( j + 1 ) * k
C( x ) 是組合數學的 C( x , 2 )
代碼: ?
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=110;const int mod=9999973;LL dp[N][N][N];//dp[i][j][k]:到了第i行,有j列沒有棋子,有k列有一個棋子的方案數 LL C(int n)//返回C(n,2)
{return (1LL*n*(n-1)/2)%mod;
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int n,m;scanf("%d%d",&n,&m);dp[0][m][0]=1;for(int i=1;i<=n;i++)for(int j=0;j<=m;j++)for(int k=0;k<=m;k++){dp[i][j][k]=dp[i-1][j][k];if(k-1>=0)dp[i][j][k]+=dp[i-1][j+1][k-1]*(j+1);dp[i][j][k]+=dp[i-1][j][k+1]*(k+1);if(k-2>=0)dp[i][j][k]+=dp[i-1][j+2][k-2]*C(j+2);dp[i][j][k]+=dp[i-1][j][k+2]*C(k+2);dp[i][j][k]+=dp[i-1][j+1][k]*(j+1)*k;dp[i][j][k]%=mod;}LL ans=0;for(int j=0;j<=m;j++)for(int k=0;k<=m;k++)if(j+k<=m)ans=(ans+dp[n][j][k])%mod;printf("%lld\n",ans);return 0;
}