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;
}

浙公网安备 33010602011771号