P8594 「KDOI-02」一个仇的复 题解

发布时间 2023-12-06 12:10:05作者: blueparrot

我会组合数!

首先发现同一列只有被不同的横块填或被一个相同的竖块填,且用竖块填完1列之后,会分成两个封闭的长方形,而长方形内部则用横块来填充。

先考虑一个子问题,某个 \(2 \times n\) 长方形内只用 \(k\)\(1 \times x\) 的横块填的方案数,显然有 \(\sum\limits^{k-1}_{i=1}{n-1\choose i-1 }{n-1\choose k-i-1}\) ,可以得到 \(2n-2\choose k-2\) ,能够扩展得到一个这样的东西: \(\sum\limits_{i=-a}^{k}{n\choose i+a}{m\choose k-i}={n+m\choose r+s}\) ,用范德蒙德卷积证明。

再考虑将竖块插进来,对于每一个横块组成的大长方形而言,除了第一个前面和最后一个后面可放可不放,其余之间都必须放至少一个竖块,相当于就是将 \(i\) 个横块插入到 \(j\) 个竖块去,即 \(j+1\choose i\) 。然后再是考虑一个个方块怎么分配给 \(i\) 个横块,直接算上 \(n-j-1\choose i-1\) 就行了,相当于是统计对于一个封闭的子长方形要给多大规模的方案数。

然后像之前所讨论过的子问题,对于 \(i\) 个子长方形,不难列出: \(\sum\limits_{\sum^{i}_{g=1}{B_g} = {k-j}}{\prod\limits^{i}_{g=1}{2A_g-2 \choose B_g-2}}\) ,其中 \(A_g\) 表示每个子长方形的列数, \(B_g\) 表示这个子长方形用多少个横块去填,组合意义优化成 \(2n-2j-2i \choose k-j-2i\)

把上面的综合一下, \(ans=\sum\limits^{k}_{i=0}\sum\limits_{j=0}^{k-i}{{j+1\choose i}{n-j-1\choose i-1}{2n-2j-2i \choose k-j-2i}}\)\(i\) 枚举横块, \(j\) 枚举竖块,时间复杂度是 \(O(k^2)\)

注意下边界,实际上 \(n=k\) 的时候会寄,因为当 \(i=0\)\(j=k\) 的时候,循环会返回0答案,所以特判一下+1就行了。

#include<bits/stdc++.h>
#define il inline 
#define ll long long 
#define maxn 40000001
using namespace std;
const ll mod=998244353;
il int read(){
	char c;int f=0,x=0;
	while(!isdigit(c=getchar()))f|=(c=='-');
	while(isdigit(c))x=(x*10)+(c^48),c=getchar();
	return f?-x:x;
}
int n,k;
int fac[maxn];
ll invfac[maxn];
il ll mypow(ll a,ll b){
	ll ret=1ll;
	while(b){
		if(b&1)ret=(ret*a)%mod;
		a=(a*a)%mod,b>>=1ll;
	}
	return ret;
}
il ll C(ll a,ll b){return a<b?0ll:fac[a]*invfac[b]%mod*invfac[a-b]%mod;}
int main(){
	n=read(),k=read();
	int nn=n*2;fac[0]=invfac[0]=1;
	for(int i=1;i<=nn;i++)fac[i]=(fac[i-1]*1ll*i*1ll)%mod;
	invfac[nn]=mypow(fac[nn]*1ll,mod-2);
	for(int i=nn-1;i>=1;i--)invfac[i]=invfac[i+1]*1ll*(i+1)%mod;
	ll ans=0;
	for(int i=1;i<=k;i++)
		for(int j=0;j<=k-i;j++){
			if(k-j-2*i<0)continue;
			ans=(ans+C(j+1,i)*C(n-j-1,i-1)%mod*C(2*n-2*j-2*i,k-j-2*i)%mod)%mod;
		}
	printf("%lld\n",ans+(n==k));
	return 0;
}