P8863 「KDOI-03」构造数组题解

数数太难。

学习的题解:Special_Judge 大神

做法

问题可以抽象为有 \(m=\frac{\sum b_i}{2}\) 个有编号二元对,然后拿 \([1,n]\) 的整数去填,\(i\) 必须出现 \(b_i\) 次,二元对内不能重复,

二元对无序。

设 \(dp_{i,j}\) 为前 \(i\) 个数有 \(j\) 个二元对被填满,考虑 \(dp_{i-1,k}\) 到 \(dp_{i,j}\) 的转移。设 \(pre_i=\sum_{1\leq p\leq i}b_p\),填了 \(pre_i\) 个数满了 \(k\) 对,也就是说 \(pre_{i-1}-2k\) 对是只填了一半的,还有 \(m-(pre_{i-1}-2k)-k=m-pre_{i-1}+k\) 对没填的。

那我们如果想加入 \(i\) 的 \(b_{i}\) 个数,且填满 \(j-k\) 个二元对,应该从只填了一半的对里选,方案数 \(\binom{pre_{i-1}-2k}{j-k}\)。剩下的应有 \(b_i-(j-k)\) 对只填了一半的,从空的里选,方案数为 \(\binom{m-pre_{i-1}+k}{b_i-(j-k)}\)。

由于三种对都不能小于 \(0\),容易得出枚举上下界:

\[L=\max\{0,j-b_i,pre_{i-1}-m\}\\ R=\min\{j,\frac{pre_{i-1}}{2}\} \]

转移:

\[dp_{i,j}=\sum_{L\leq k\leq R} dp_{i-1,k}\binom{m-pre_{i-1}+k}{b_i-(j-k)}\binom{pre_{i-1}-2k}{j-k} \]

#include <bits/stdc++.h>
#define int int64_t
//#define int __int128
#define MOD (998244353)
//#define eps (1e-6)
#define endl '\n'
#define debug_endl cout<<endl;
#define debug cout<<"debug"<<endl;
namespace Easy_Count{
	const int _MAXN=2e5+10;
	int inv[_MAXN],jc[_MAXN];
	template<typename T>
	inline int mod_plus(T val) { return val % MOD; }
	template<typename T, typename... Rest>
	inline int mod_plus(T first, Rest... rest) {
		return (static_cast<long long>(first % MOD) + mod_plus(rest...)) % MOD;
	}
	template<typename T>
	inline int mod_mul(T val) { return val % MOD; }
	template<typename T, typename... Rest>
	inline int mod_mul(T first, Rest... rest) {
		return (static_cast<long long>(first % MOD) * mod_mul(rest...)) % MOD;
	}
	inline int mod(int x){
		return (x%MOD+MOD)%MOD;
	}
	inline int qpow(int a,int b){
		int res=1;
		while(b){
			if(b&1){
				res=(res*a)%MOD;
			}
			a=(a*a)%MOD;
			b>>=1;
		}
		return res;
	}
	inline int C(int n,int m){
		if(n < 0 || m < 0 || n < m) return 0;
		return mod_mul(jc[n],inv[n-m],inv[m]);
	}
	inline void init_binom(int n){
		jc[0]=1;
		for(int i=1;i<=n;++i){
			jc[i]=(jc[i-1]*i)%MOD;
		}
		inv[n]=qpow(jc[n],MOD-2);
		for(int i=n-1;i>=0;--i){
			inv[i]=(inv[i+1]*(i+1))%MOD;
		}
	}
}
using namespace Easy_Count;
using namespace std;
int n,b[5010],pre[5010];
int dp[2][15010];
signed main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>b[i];
		pre[i]=pre[i-1]+b[i];
	}
	int m=pre[n];
	if(m&1||n==1){
		cout<<0;
		return 0;
	}
	m/=2;
	init_binom(m*2);
	dp[0][0]=1;
	for(int i=1;i<=n;++i){
		for(int j=0;j<=m;++j) dp[i&1][j]=0;
		for(int j=0;j<=m;++j){
			int L=max((int)0,max(j-b[i],pre[i-1]-m));
			int R=min(j,pre[i-1]/2);
			for(int k=L;k<=R;++k){
				dp[i&1][j]=mod_plus(dp[i&1][j],mod_mul(dp[(i&1)^1][k],C(m-pre[i-1]+k,b[i]-j+k),C(pre[i-1]-2*k,j-k)));
			}
		}
	}
	cout<<dp[n&1][m];
	return 0;
}
posted @ 2026-10-25 23:29  司马只因锥  阅读(3)  评论(0)    收藏  举报