[Codeforces1237F]Balanced Domino Placements

题意

给你一个 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 1n,m3600,0k2400


题解

假设我们放了 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(0j2i)个纵向骨牌的方案数,那么有

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(i1,j)f(i1,j)+f(i2,j1)j=0i,i1行有一行被占了i,i1行均未被占

如果有总共有 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)(dxh2dy)

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=dxdyf(n,dy)g(m,dx)(dxn2dy)(dym2dx)dx!dy!             =dxdyf(n,dy)g(m,dx)Pn2dydxPm2dxdy
其中 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' 0dx+2dyn,02dx+dym

时间复杂度 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值