abc242 F - Black and White Rooks

放置事实上等同于 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;
}

posted @ 2022-03-06 12:00  FxorG  阅读(114)  评论(0编辑  收藏  举报