abc242 F - Black and White Rooks
阿新 • • 發佈:2022-03-06
放置事實上等同於 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; }