题意:给了n*m的网格,然后有p个重型的防御塔,能承受1次攻击,q个轻型防御塔不能接受任何攻击,然后每个防御搭会攻击他所在的行和所在的列,最后求在这个网格上放至少一个防御塔的方案数,
我们枚举 选取多少个重型防御塔然后这个重型防御塔有多少是两个在一行,和两个在一列 O(P^3)的效率
#include <iostream> #include <algorithm> #include <cstdio> #include <string.h> #include <vector> using namespace std; typedef long long LL; const LL MOD=1000000007; const int maxn=202; LL C[maxn][maxn];//组合数 LL light[maxn][maxn][maxn];// light[i][j][k]k栈轻量级的防御塔放在i行j列的矩阵的方案数 LL heavy[maxn];// haeavy[i] 表示有i个两个同一行的重量级的方案数 LL Nt[maxn];//N! n阶乘 void init() { Nt[0]=1; for(LL i=1; i<=200; i++) Nt[i]=(Nt[i-1]*i)%MOD; memset(C,0,sizeof(C)); C[0][0]=1; for(int i=1; i<=200; i++) { C[i][0]=1; for(int j=1; j<=i; j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD; } for(int i=1; i<=200; i++) for(int j=1; j<=200; j++) { int kM=min(i,j); light[ i ][ j ][ 0 ] = 1; for(int k=1; k<=kM; k++) { light[ i ][ j ][ k ]=( ( (C[ i ][ k ]*C[ j ][ k ])%MOD )*Nt[k])%MOD; } } for(int i=1; i<=200; i++) for(int j=1; j<=200; j++) { int kM=min(i,j); for(int k=1; k<=kM; k++) light[i][j][k]=(light[i][j][k-1]+light[i][j][k])%MOD; } heavy[0]=1; for(int i=1; i<=100; i++) { LL ans=1; for(int j=i*2; j>2; j-=2 ) { ans=(C[j][2]*ans)%MOD; } heavy[i]=ans; } } int main() { init(); int N,M,P,Q; int cas; scanf("%d",&cas); for(int cc=1; cc<=cas; cc++) { scanf("%d%d%d%d",&N,&M,&P,&Q); LL ans=0; for(int k=1; k<=P; k++) for(int i=0; i<=k; i+=2) for(int j=0; j+i<=k; j+=2) { int LN=N-i/2-j; int LM=M-j/2-i; if(min(LN,LM)<k-(i+j) )continue; LN=N,LM=M; LL d=1; d=( ( ( C[LN][i/2]*C[LM][i] )%MOD )*heavy[i/2])%MOD; LN-=i/2; LM-=i; d=( d*( ( ( ( C[LN][j] * C[LM][j/2] )%MOD ) * heavy[j/2] )%MOD ) )%MOD; LN-=j; LM-=j/2; int lest=k-(i+j); d= ( ( ( d*( ( C[LN][lest]*C[LM][lest] )%MOD ))%MOD)*Nt[lest] )%MOD; LN-=lest;LM-=lest; if(LN>0&&LM>0) { int ge=min(min(LN,LM),Q); d=(d*(light[LN][LM][ge]))%MOD; } ans=(ans+d)%MOD; } if(Q>0) { int ge=min(min(N,M),Q); ans=(ans+light[N][M][ge])%MOD; ans=(ans-1+MOD)%MOD; } printf("%I64d ",ans%MOD); } return 0; }