[容斥原理][HDU 6314][2018杭电多校训练2 G题] Matrix
题目大意
给定一个(N imes M)的矩阵,要将矩阵的每个格子涂成白色或黑色,问使得矩阵至少有(A)行(B)列被完全涂成黑色的方案数。
题解
设(f[N][M])表示(N imes M)的矩阵任意行任意列都没有被完全涂成黑色的方案数。由容斥原理
(f[N][M]=sum_{r=0}^{N}sum_{c=0}^{M} C_N^r imes C_M^c imes (-1)^{r+c} imes 2^{(N-r)*(M-c)})
(Ans=sum_{x=A}^{N}sum_{y=B}^{M} C_N^x imes C_M^y imes f[N-x][M-y])
(=sum_{x=A}^{N}sum_{y=B}^{M} C_N^x imes C_M^y imes(sum_{r=0}^{N-x}sum_{c=0}^{M-y} C_{N-x}^r imes C_{M-y}^c imes (-1)^{r+c} imes 2^{(N-x-r)(M-y-c)}))
(=sum_{x=A}^{N}sum_{y=B}^{M}sum_{r=0}^{N-x}sum_{c=0}^{M-y} C_N^x imes C_M^y imes C_{N-x}^r imes C_{M-y}^c imes (-1)^{r+c} imes 2^{(N-x-r)(M-y-c)})
(C_N^x imes C_M^y imes C_{N-x}^r imes C_{M-y}^c=frac{N!}{x! imes (N-x)!} imes frac{M!}{y! imes (M-y)!} imes frac{(N-x)!}{r! imes (N-x-r)!} imesfrac{(M-y)!}{c! imes (M-y-c)!})
(=frac{N! imes M!}{x! imes y! imes r! imes c! imes p! imes q!})
令(p=N-x-r,q=M-y-c),则
(Ans=sum_{x=A}^{N}sum_{y=B}^{M}sum_{r=0}^{N-x}sum_{c=0}^{M-y} frac{N! imes M!}{x! imes y! imes r! imes c! imes p! imes q!} imes (-1)^{r+c} imes 2^{pq})
(=sum_{x=A}^{N}sum_{y=B}^{M}sum_{r=0}^{N-x}sum_{c=0}^{M-y} frac{2^{pq}}{p! imes q!} imes frac{N! imes(-1)^r}{x! imes r!} imes frac{M! imes (-1)^c}{y! imes c!})
(x+r=N-p, y+c=M-q,Aleq x, Bleq y)
设(Pre[s][A]) 表示满足(x+r=s,Aleq x)的(frac{(-1)^r}{x! imes r!})的和,令(g[p][q]=frac{2^{pq}}{p! imes q!}),则
(Ans=N! imes M! imes sum_{p=0}^{N-A}sum_{q=0}^{M-B}Pre[N-p][A] imes Pre[M-q][B] imes g[p][q])
时间复杂度(O(NM))。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define RG register int
#define LL long long
const LL MOD=998244353;
LL Inv[3010],InvFact[3010],Fact[3010],Pre[3010][3010];
int Pow2[9000010],g[3010][3010];
int N,M,A,B;
inline void Init(){
Inv[1]=1;InvFact[1]=1;Fact[1]=1;
InvFact[0]=Fact[0]=1;Pow2[0]=1;
for(RG i=2;i<=3000;++i){
Inv[i]=(MOD-MOD/i)*Inv[MOD%i]%MOD;
InvFact[i]=(InvFact[i-1]*Inv[i])%MOD;
Fact[i]=(Fact[i-1]*(LL)i)%MOD;
}
for(RG i=1;i<9000000;++i){
Pow2[i]=Pow2[i-1]<<1;
if(Pow2[i]>MOD) Pow2[i]-=MOD;
}
return;
}
inline void GetPre(){
for(RG x=3000;x>=0;--x){
for(RG r=0;r<=3000-x;++r){
LL temp;
if(r&1) temp=MOD-1;
else temp=1;
Pre[x+r][x]=(Pre[x+r][x]+((temp*InvFact[x])%MOD)*InvFact[r]%MOD)%MOD;
Pre[x+r][x-1]=(Pre[x+r][x-1]+Pre[x+r][x])%MOD;
}
for(RG y=0;y<=3000;++y)
g[x][y]=Pow2[x*y]*InvFact[x]%MOD*InvFact[y]%MOD;
}
return;
}
inline void Solve(){
LL Ans=0;
for(RG p=0;p<=N-A;++p)
for(RG q=0;q<=M-B;++q){
Ans=(Ans+(Pre[N-p][A]*Pre[M-q][B]%MOD)*g[p][q]%MOD);
if(Ans>MOD) Ans-=MOD;
}
Ans=(((Ans*Fact[N])%MOD)*Fact[M])%MOD;
printf("%lld
",Ans);
return;
}
int main(){
Init();
GetPre();
while(~scanf("%d%d%d%d",&N,&M,&A,&B))
Solve();
return 0;
}