题目
http://acm.hdu.edu.cn/showproblem.php?pid=4779
分析
重塔可以放成 ‘++’ 型, 轻塔和重塔都可以放成 ‘十’ 型。
用f[i][j][k]表示i*j的棋盘,没有空行空列,一共放了k个‘十’的方案数。这时 组成‘++’ 型的塔有(i+j)*2/3 因为一个‘++’占一行两列。
转移时考虑插入一个‘十’或一个‘++’ 还有反过来的 ‘++’ 。规定必须有塔放在顶格,防止重复。
考虑顺推,将f[i][j][k]的值转给后面的状态
即
f[i+1][j+1][k+1]+=1ll*(j+1)*f[i][j][k];
f[i+1][j+2][k]+=1ll*c[j+2][2]*f[i][j][k];
f[i+2][j+1][k]+=1ll*(i+1)*(j+1)*f[i][j][k];
然后这题必须加许多常数优化,比如f[i][j][k]=0则不转移,还有在循环时就将无效状态的上下界卡掉,。。。!!!
代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; typedef long long int64; const int MOD=1000000007; int f[210][210][210],ans,n,m,p,q,T; int s[210][210],c[210][210]; inline void update(int &a,int64 b) { a+=b; if(a>=MOD) a-=MOD; } int main() { for(int i=0;i<=200;++i) c[i][0]=1; for(int i=1;i<=200;++i) for(int j=1;j<=i;++j) c[i][j]=c[i-1][j-1]+c[i-1][j],update(c[i][j],0); for(int i=0;i<=200;++i) { s[i][0]=1; for(int j=1;j<=i;++j) s[i][j]=s[i][j-1]+c[i][j],update(s[i][j],0); } f[0][0][0]=1; for(int i=0;i<=200;++i) for(int j=i/2;j<=200;++j) for(int k=0;k<=min(i,j);++k) { update(f[i+1][j+1][k+1],1ll*(j+1)*f[i][j][k]%MOD); update(f[i+1][j+2][k],1ll*c[j+2][2]*f[i][j][k]%MOD); update(f[i+2][j+1][k],1ll*(i+1)*(j+1)*f[i][j][k]%MOD); } scanf("%d",&T); while(T--) { scanf("%d%d%d%d",&n,&m,&p,&q); ans=0; for(int i=1;i<=min(n,p+q);++i) for(int j=i/2;j<=min(m,p+q);++j) { for(int k=min(i,j)-abs(j-i)/2;k>=0;--k) { if((i+j-2*k)%3) continue; if((i+j-2*k)*2/3>p) break; int a=(i+j-2*k)*2/3,b=k; if(p-a<b-q) break; if(!f[i][j][k]) continue; update(ans,1ll*(s[b][min(b,p-a)]-((b-q-1)>=0?s[b][b-q-1]:0)+MOD)*c[n][i]%MOD*c[m][j]%MOD*f[i][j][k]%MOD); } } printf("%d\n",ans); } return 0; }