洛谷P2051 [AHOI2009]中國象棋
阿新 • • 發佈:2019-01-10
連結
題解
很厲害的題目。
首先這個問題就是在N*M的網格中放若干個棋子使得每一行每一列至多有2個棋子。
考慮xjb暴力,30%的資料
觀察最大的資料
轉移有點龐大,直說一個吧,當j>=1時,就是說有j個位置上是0,那麼下一行裡就可以在對應位置放一個棋子使得這個0變成1,所以轉移到f[i+1][j-1][k+1],而你要從j個0中選擇任意一個,所以f[i+1][j-1][k+1]+=f[i][j][k]*j,其餘的同理。具體程式碼中有註釋。
程式碼
//動態規劃+排列
#include <cstdio>
#include <algorithm>
#define maxn 110
#define mod 9999973ll
#define ll long long
using namespace std;
ll N, M, f[maxn][maxn][maxn], fact[maxn], a[maxn], b[maxn];
void exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b){x=1,y=0;return;}
ll xx, yy;
exgcd(b,a%b ,xx,yy);
x=yy, y=xx-a/b*yy;
}
ll inv(ll a, ll p=mod)
{
ll x, y;
exgcd(a,p,x,y);
return (x+p)%p;
}
void init()
{
ll i;
fact[0]=1;
for(i=1;i<=100;i++)fact[i]=(fact[i-1]*i)%mod;
for(i=0;i<=100;i++)a[i]=fact[i], b[i]=inv(fact[i]);
}
void dp()
{
ll i, j, k, t, ans=0;
f[0 ][N][0]=1;
for(i=0;i<=M;i++)
{
for(j=0;j<=N;j++)
{
for(k=0;j+k<=N;k++)
{
t=f[i][j][k]%mod;
f[i+1][j][k]+=t;
if(j>=1)f[i+1][j-1][k+1]+=t*j; //0->1
if(j>=2)f[i+1][j-2][k+2]+=t*j*(j-1)/2;//0->1,0->1
if(k>=1)f[i+1][j][k-1]+=t*k; //1->2
if(k>=2)f[i+1][j][k-2]+=t*k*(k-1)/2; //1->2,1->2
if(j>=1 and k>=1)f[i+1][j-1][k]+=t*j*k; //0->1,1->2
}
}
}
for(j=0;j<=N;j++)for(k=0;j+k<=N;k++)ans=(ans+f[M][j][k])%mod;
printf("%lld",ans);
}
int main()
{
scanf("%lld%lld",&N,&M);
init();
dp();
return 0;
}