CF2151D 题解

先看同时满足 \(2\)\(3\) 条件的可能的黑格位置(用 # 表示),可以发现当 \(n=5\) 时是这样的:

#####
.###.
..#..
.....
.....

现在在可能的位置考虑,若现在在第 \(i\) 行还有 \(k\) 个格子可以放黑格,那么方案数就为 \(C_k^{a_i}\),但 \(2\)\(3\) 条件是恰好有一个格子,所以要减去之前已经放的位置数。

由于 \(i-1\) 行可能放的列一定包括 \(i\) 行的,所以我们从大往小统计答案,结果为:

\[\prod_{i=1}^n C_{k-suf_{i+1}}^{a_i} \]

\(suf_i\) 表示 \(\sum_{j=i}^n a_i\)

#include<bits/stdc++.h>
#define N 200005
using namespace std;
const int mod=998244353;
int n,sum,ans,a[N],fac[N],ifac[N];
int qpow(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;
}
static int C(int x,int y){
	return x>y?0:1ll*fac[y]*ifac[x]%mod*ifac[y-x]%mod;
}
int main(){
	fac[0]=1;
	for(int i=1;i<=N-5;i++)
		fac[i]=1ll*fac[i-1]*i%mod;
	ifac[N-5]=qpow(fac[N-5],mod-2);
	for(int i=N-5;i>=1;i--)
		ifac[i-1]=1ll*ifac[i]*i%mod;
	/*预处理阶乘及逆元以求组合数*/
	int T;scanf("%d",&T);
	while(T--){
		scanf("%d",&n),sum=0,ans=1;
		long long check=0;
		for(int i=1;i<=n;i++)
			scanf("%d",a+i),check+=a[i];
		if(check<n) ans=0;//特判无解 
		for(int i=n;i>=1&&ans;sum+=a[i--]){
			int lim=max(n-(i-1<<1),0);//该行可以放黑格的位置的数量 
			if(sum>lim) ans=0;//特判无解 
			ans=1ll*ans*C(a[i],lim-sum)%mod;//真正的数量要减去前面放过的(一列只能放一个) 
		}
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2025-11-22 11:23  Jokersen  阅读(0)  评论(0)    收藏  举报