题意
给你一个 n × m n\times m n×m的棋盘,上面已经放了 k k k个的骨牌( 1 × 2 1\times2 1×2)
对于一个骨牌的每个格子,不能有其他骨牌的格子和它在同一列,同一行
你可以在剩下的格子里放骨牌,也可以不放
问这个棋盘有多少种放骨牌的方案,对 998244353 998244353 998244353取模
1 ≤ n , m ≤ 3600 , 0 ≤ k ≤ 2400 1\le n,m\le3600,0\le k\le2400 1≤n,m≤3600,0≤k≤2400
题解
假设我们放了 d x d_x dx个横向的骨牌, d y d_y dy个纵向的骨牌
如果某一行已经被占了,将其标记为 1 1 1,否则为 0 0 0,列也是一样
设 R R R表示至少有 d x + 2 d y d_x+2d_y dx+2dy行 0 0 0且有 d y d_y dy对相邻的 0 0 0的方案数, C C C表示至少有 2 d x + d y 2d_x+d_y 2dx+dy列 0 0 0且有 d x d_x dx对相邻的 0 0 0的方案数
因为 d x d_x dx行放了横向的骨牌,且每行的骨牌有 d x d_x dx个位置可以选择(纵向同理)
那么 A n s = R × C × d x ! × d y ! Ans=R\times C\times d_x!\times d_y! Ans=R×C×dx!×dy!
考虑怎么求 R R R,记 f ( i , j ) f(i,j) f(i,j)表示前 i i i行放了 j ( 0 ≤ j ≤ ⌊ i 2 ⌋ ) j(0\le j\le\lfloor\frac i2\rfloor ) j(0≤j≤⌊2i⌋)个纵向骨牌的方案数,那么有
f ( i , j ) = { 1 j = 0 f ( i − 1 , j ) 第 i , i − 1 行有一行被占了 f ( i − 1 , j ) + f ( i − 2 , j − 1 ) 第 i , i − 1 行均未被占 f(i,j)= \begin{cases} 1&\text{$j=0$}\\ f(i-1,j) &\text{第$i,i-1$行有一行被占了}\\ f(i-1,j)+f(i-2,j-1)&\text{第$i,i-1$行均未被占} \end{cases} f(i,j)=⎩⎪⎨⎪⎧1f(i−1,j)f(i−1,j)+f(i−2,j−1)j=0第i,i−1行有一行被占了第i,i−1行均未被占
如果有总共有 h h h行 0 0 0,那么 R = f ( h , d y ) ( h − 2 d y d x ) R=f(h,d_y){h-2d_y\choose d_x} R=f(h,dy)(dxh−2dy)
C C C可以按照同样的方式计算(设为 g ( i , j ) g(i,j) g(i,j)。 f f f和 g g g的 d p dp dp可以用滚动数组优化)
设 n ′ n' n′为没被占的行数, m ′ m' m′为没被占的列数,则
A
n
s
=
∑
d
x
∑
d
y
f
(
n
′
,
d
y
)
g
(
m
′
,
d
x
)
(
n
′
−
2
d
y
d
x
)
(
m
′
−
2
d
x
d
y
)
d
x
!
d
y
!
=
∑
d
x
∑
d
y
f
(
n
′
,
d
y
)
g
(
m
′
,
d
x
)
P
n
′
−
2
d
y
d
x
P
m
′
−
2
d
x
d
y
Ans=\sum_{d_x}\sum_{d_y}f(n',d_y)g(m',d_x){n'-2d_y\choose d_x}{m'-2d_x\choose d_y}d_x!d_y!\\ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{d_x}\sum_{d_y}f(n',d_y)g(m',d_x)P^{d_x}_{n'-2d_y}P^{d_y}_{m'-2d_x}
Ans=dx∑dy∑f(n′,dy)g(m′,dx)(dxn′−2dy)(dym′−2dx)dx!dy! =dx∑dy∑f(n′,dy)g(m′,dx)Pn′−2dydxPm′−2dxdy
其中
0
≤
d
x
+
2
d
y
≤
n
′
,
0
≤
2
d
x
+
d
y
≤
m
′
0\le d_x+2d_y\le n',0\le 2d_x+d_y\le m'
0≤dx+2dy≤n′,0≤2dx+dy≤m′
时间复杂度 O ( n m ) O(nm) O(nm)
#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
const int N=4000+5,Mod=998244353;
typedef int arr[N];
int n,m,k,ans;arr fac,ifac;
inline int Plus(int a,int b){return a+b-(a+b>Mod?Mod:0);}
inline int Mul(int a,int b){return (long long)a*b%Mod;}
inline int P(int n,int m){return Mul(fac[n],ifac[n-m]);}
inline int fpm(int a,int b){
int x=1;
for(;b;a=Mul(a,a),b>>=1)
if(b&1)x=Mul(x,a);
return x;
}
inline void GetFact(int S){
fac[0]=1;
fp(i,1,S)fac[i]=Mul(fac[i-1],i);
ifac[S]=fpm(fac[S],Mod-2);
fd(i,S,1)ifac[i-1]=Mul(ifac[i],i);
}
vector<int>dp(const vector<bool>&u){
int n=u.size()-1;
vector<int>f1(n/2+1),f2(n/2+1),f3;
f2[0]=1;
fp(i,1,n){
f3=f2;
if(!u[i]&&!u[i-1])
fp(j,1,i/2)f3[j]=Plus(f3[j],f1[j-1]);
f1=f2;f2=f3;
}
return f2;
}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
sd(n),sd(m),sd(k);
vector<bool>ux(n+1),uy(m+1);
fp(i,1,2*k){
int x,y;
sd(x),sd(y);
ux[x]=uy[y]=1;
}
fp(i,1,n)if(ux[i])--n;
fp(i,1,m)if(uy[i])--m;
GetFact(max(n,m)+1);
vector<int>f=dp(ux);
vector<int>g=dp(uy);
fp(dy,0,(int)f.size()-1)
for(int dx=0;dx<(int)g.size()&&dx+2*dy<=n&&2*dx+dy<=m;++dx)
ans=Plus(ans,Mul(Mul(f[dy],g[dx]),Mul(P(n-(dy<<1),dx),P(m-(dx<<1),dy))));
printf("%d",ans);
return 0;
}