题目传送门:bzoj4806
这种题一看就是dp。。。我们可以设$ f[i][j][k] $表示处理到第$ i $行,有$ j $列没放炮,$ k $列只放了一个炮。接着分情况讨论:第$ i $行不放炮、放一个炮、放两个炮;放在只有一个炮的列上,还是放在没炮的列上。于是就可以快乐地列方程了:
$ \begin{equation} \begin{split} f[i][j][k] &= f[i-1][j][k] \\ &+ (j+1)\cdot f[i-1][j+1][k-1] \\ &+ (k+1)\cdot f[i-1][j][k+1] \\ &+ \binom{j+2}{2} \cdot f[i-1][j+2][k-2] \\ &+ (j+1)\cdot k\cdot f[i-1][j+1][k] \\ &+ \binom{k+2}{2}\cdot f[i-1][j][k+2] \end{split} \end{equation} $
另外,此题有双倍经验:bzoj1801(我是不会告诉你模数不一样的)
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<ctime> #include<algorithm> #include<queue> #include<vector> #define ll long long #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #define inf 0x7fffffff #define mod 999983 #define eps 1e-20 ll read() { ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=tmp*10+c-'0'; return tmp*f; } using namespace std; ll f[110][110][110]; int n,m; int main() { int i,j,k; n=read(); m=read(); f[0][m][0]=1; for(i=1;i<=n;i++) for(j=0;j<=m;j++) for(k=0;k+j<=m;k++){ f[i][j][k]=f[i-1][j][k]+f[i-1][j][k+1]*(k+1)+f[i-1][j][k+2]*(k+2)*(k+1)/2+f[i-1][j+1][k]*k*(j+1); if(k)f[i][j][k]+=f[i-1][j+1][k-1]*(j+1); if(k>1)f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2; f[i][j][k]%=mod; } ll ans=0; for(j=0;j<=m;j++) for(k=0;k+j<=m;k++) ans=(ans+f[n][j][k])%mod; printf("%lld\n",ans); }