放置事实上等同于 2 种颜色所覆盖到的行、列集合没交。
考虑枚举 2 种颜色各自放置的行列。
\[\sum_{i,j}f[i][j]*g[i][j]*\binom{n}{i+x}*\binom{i+x}{i}*\binom{m}{j+y}*\binom{j+y}{j}
\]
\(f[i][j]\) 为选择的黑色点仅包含 \(i\) 行 \(j\) 列的方案数。
考虑仅包含,那么显然选择的是 \(i\) 行 \(j\) 列交的矩形,即 \(f[i][j]=\binom{ij}{B}\)。
考虑对于一个小的方案可能会在大的方案贡献到,那么我们所选择的必须没有一行、一列是空的。这个的话发现是恰好,可以类似于 这道题,直接用二项式反演。
或者使用递推,减去实际占的行列不够的。
二项式反演
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
#define N 2502
const int mod=998244353;
int fpow(int x,int y) {
int res=1;
while(y) {
if(y&1) res=res*x%mod; y>>=1; x=x*x%mod;
}
return res;
}
int f[52][52],g[52][52],a[52][52],b[52][52],jie[N],djie[N],n,m,B,W;
int C(int n,int m) {
if(m>n||n<=0||m<0) return 0;
return jie[n]*djie[m]%mod*djie[n-m]%mod;
}
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
jie[0]=djie[0]=1; for(int i=1;i<=2500;i++) jie[i]=jie[i-1]*i%mod,djie[i]=fpow(jie[i],mod-2);
cin>>n>>m>>B>>W;
for(int x=1;x<=n;x++) {
for(int y=1;y<=m;y++) {
for(int i=0;i<=x;i++) {
for(int j=0;j<=y;j++) {
a[i][j]=C(x,i)*C(y,j)%mod*C(x*y-(i*y+j*x-i*j),B)%mod;
}
}
int qwq=0;
for(int i=0;i<=x;i++) {
for(int j=0;j<=y;j++) {
qwq=(qwq+(((i+j)&1)?-1:1)*a[i][j]%mod)%mod;
}
}
f[x][y]=qwq;
// cout<<qwq<<' ';
}
// cout<<'\n';
}
for(int x=1;x<=n;x++) {
for(int y=1;y<=m;y++) {
for(int i=0;i<=x;i++) {
for(int j=0;j<=y;j++) {
a[i][j]=C(x,i)*C(y,j)%mod*C(x*y-(i*y+j*x-i*j),W)%mod;
}
}
int qwq=0;
for(int i=0;i<=x;i++) {
for(int j=0;j<=y;j++) {
qwq=(qwq+(((i+j)&1)?-1:1)*a[i][j]%mod)%mod;
}
}
g[x][y]=qwq;
// cout<<qwq<<' ';
}
// cout<<'\n';
}
int ans=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
for(int x=1;x<=n;x++) {
for(int y=1;y<=m;y++) {
ans=(ans+f[i][j]*g[x][y]%mod*C(n,i+x)%mod*C(i+x,i)%mod*C(m,j+y)%mod*C(j+y,j)%mod)%mod;
}
}
}
}
ans=(ans%mod+mod)%mod; cout<<ans;
return 0;
}
递推
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
#define N 2502
const int mod=998244353;
int fpow(int x,int y) {
int res=1;
while(y) {
if(y&1) res=res*x%mod; y>>=1; x=x*x%mod;
}
return res;
}
int f[52][52],g[52][52],jie[N],djie[N],n,m,b,w;
int C(int n,int m) {
if(m>n||n<=0||m<0) return 0;
return jie[n]*djie[m]%mod*djie[n-m]%mod;
}
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
jie[0]=djie[0]=1; for(int i=1;i<=2500;i++) jie[i]=jie[i-1]*i%mod,djie[i]=fpow(jie[i],mod-2);
cin>>n>>m>>b>>w;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
f[i][j]=C(i*j,b);
// if(!f[i][j]) continue ;
for(int x=1;x<=i;x++) {
for(int y=1;y<=j;y++) {
if(x==i&&y==j) continue ;
f[i][j]=(f[i][j]-f[x][y]*C(i,x)%mod*C(j,y)%mod)%mod;
}
}
// if(f[i][j]<0) f[i][j]=0;
// cout<<f[i][j]<<" ";
}
// cout<<'\n';
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
g[i][j]=C(i*j,w);
// if(!g[i][j]) continue ;
for(int x=1;x<=i;x++) {
for(int y=1;y<=j;y++) {
if(x==i&&y==j) continue ;
g[i][j]=(g[i][j]-g[x][y]*C(i,x)%mod*C(j,y)%mod)%mod;
}
}
// if(g[i][j]<0) g[i][j]=0;
// cout<<g[i][j]<<" ";
}
// cout<<'\n';
}
int ans=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
for(int x=1;x<=n;x++) {
for(int y=1;y<=m;y++) {
ans=(ans+f[i][j]*g[x][y]%mod*C(n,i+x)%mod*C(i+x,i)%mod*C(m,j+y)%mod*C(j+y,j)%mod)%mod;
}
}
}
}
ans=(ans%mod+mod)%mod; cout<<ans;
return 0;
}