P8114 [Cnoi2021]六边形战士 gxy001

很显然,题目等价于将三角形网格划分为若干个菱形的方案数,每个菱形由两个三角形组成,而这个方案又与 $a\times b\times c$ 的长方体中,堆叠了一些靠一墙角放置的 $1\times 1\times 1$ 的小正方体的方案数相等。

考虑每个位置的高度,问题等价于,给一个 $a\times b$ 的网格 $h$ 内填数,每个位置可以填 $[0,c]$ 中的整数,且要满足 $h_{i,j}\le h_{i+1,j}\land h_{i,j}\le h_{i,j+1}$ 的方案数。

设 $g_{i,j}=h_{i,j}+i$,则有 $g_{i,j}< g_{i+1,j}\land g_{i,j}\le g_{i,j+1}$,且所有满足 $g_{i,j}< g_{i+1,j}\land g_{i,j}\le g_{i,j+1}$ 且每个位置都在 $[1,a+c]$ 中的 $g$ 与 $h$ 一一对应。

我们发现,$g$ 是一个半标准杨表,根据钩长公式,我们知道,其填数方案数即为

\[\prod\limits_{i=1}^a\prod\limits_{j=1}^b\frac{a+c+j-i}{a-i+b-j+1}\\ =\prod\limits_{i=1}^a\prod\limits_{j=1}^b\frac{(a+c-i)+j}{(a+b-i+1)-j}\\ =\prod\limits_{i=1}^a\frac{(a+c+b-i)!(a-i)!}{(a+c-i)!(a+b-i)!}\\ =\frac{f(a+b+c-1)f(a-1)f(c-1)f(b-1)}{f(b+c-1)f(a+c-1)f(a+b-1)}\\ f(n)=\prod\limits_{i=1}^ni!\]

预处理 $O(a+b+c)$,单次询问 $O(1)$。

#include<iostream>
using std::cin;
using std::cout;
int const mod=998244353;
int fac[3000010],f[3000010],iv[3000010],a,b,c;
int pow(int x,int y){
	int res=1;
	while(y){
		if(y&1) res=1ll*res*x%mod;
		x=1ll*x*x%mod,y>>=1;
	}
	return res;
}
int main(){
	std::ios::sync_with_stdio(false),cin.tie(nullptr);
	cin>>a>>b>>c;
	int n=a+b+c;
	fac[0]=f[0]=1;
	for(int i=1;i<=n;i++) fac[i]=1ll*i*fac[i-1]%mod;
	for(int i=1;i<=n;i++) f[i]=1ll*f[i-1]*fac[i]%mod;
	iv[n]=pow(f[n],mod-2);
	for(int i=n;i;i--) iv[i-1]=1ll*iv[i]*fac[i]%mod;
	cout<<(1ll*f[n-1]*f[a-1]%mod*f[b-1]%mod*f[c-1]%mod*iv[a+b-1]%mod*iv[a+c-1]%mod*iv[b+c-1]%mod)<<'\n';
	return 0;
}