最近智商有点不在线。其实一直不在线。
先是想用$f[i][j][k][0/1/2]$表示摆了i行时有j列空着,k列有了一个炮,且当下摆了0/1/2个的状态,转移方程写的出来但是极其繁琐。于是又设法听取评讲者题解修改状态,最后的012完全可以删去。那么仍可以表示这一行那些列摆过1个,那些列摆过0个的种类。转移时分类即可。
$f[i][j][k]+=f[i-1][j][k]$ 什么都不摆
$f[i][j][k]+=(j+1)*f[i-1][j+1][k-1]$ 摆1个炮
$f[i][j][k]+=(k+1)*f[i-1][j][k+1]$ 摆1个炮
$f[i][j][k]+=(j+1)*(j+2)/2*f[i-1][j+2][k-2]$ 摆两个炮,下同
$f[i][j][k]+=(k+1)*(k+2)/2*f[i-1][j][k+2]$
$f[i][j][k]+=k*(j+1)*f[i-1][j+1][k]$
注意边界就行。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef pair<int,int> pii; 5 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;} 6 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;} 7 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 8 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 9 template<typename T>inline T read(T&x){ 10 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 11 while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();return f?x=-x:x; 12 } 13 const int N=100+7,P=9999973; 14 int n,m,ans,f[N][N][N]; 15 16 inline void inc(int&x,int y){(x+=y)>=P?x-=P:1;} 17 18 int main(){//freopen("tmp.in","r",stdin);freopen("tmp.out","w",stdout); 19 read(n),read(m);if(m<2){printf("%d\n",1+n+(n)*(n-1)/2);return 0;} 20 f[1][m][0]=1,f[1][m-1][1]=m,f[1][m-2][2]=m*(m-1)/2; 21 for(register int i=2;i<=n;++i) 22 for(register int j=0;j<=m;++j) 23 for(register int k=0;k<=m-j;++k){ 24 inc(f[i][j][k],f[i-1][j][k]); 25 if(k)inc(f[i][j][k],(j+1)*f[i-1][j+1][k-1]%P); 26 inc(f[i][j][k],(k+1)*f[i-1][j][k+1]%P); 27 if(k>=2)inc(f[i][j][k],((j+1)*(j+2)>>1)*1ll*f[i-1][j+2][k-2]%P); 28 inc(f[i][j][k],((k+1)*(k+2)>>1)*1ll*f[i-1][j][k+2]%P); 29 inc(f[i][j][k],1ll*k*(j+1)*f[i-1][j+1][k]%P); 30 } 31 for(register int i=0;i<=m;++i)for(register int j=0;j<=m-i;++j)inc(ans,f[n][i][j]); 32 printf("%d\n",ans); 33 return 0; 34 }