「ABC231 G」 Balls in Boxes 题解
\(~~~~\) 终于有一道题让我有动力起魔怔标题了。
题意
\(~~~~\) \(n\) 个盒子,初始第 \(i\) 个盒子有 \(a_i\) 个球,将 \(k\) 个球依次独立随机放入 \(n\) 个盒子内。定义最终状态的权值为所有盒子内球数量的积,求期望权值。
\(~~~~\) \(1\leq n\leq 1000,1\leq k\leq 10^9\)。
题解
\(~~~~\) 期望很假,由于放球共有 \(n^k\) 种情况这很好求,所以问题在于 \(n^k\) 种情况的权值和。
\(~~~~\) 假设所有 \(a_i=0\) 怎么做,记 \(x_{i,j}\in\{0,1\}\) 表示第 \(i\) 个盒子内是否放了球 \(j\),那贡献就可以表示为:
\(~~~~\) 展开的话就会是若干形似这样的项之和:
\(~~~~\) 显然当所有 \(x\) 都取 \(1\) 时其才会产生贡献,更进一步这里取到的 \(t_i\) 应该互不相同。那合法的 \(\{t_i\}\) 就应该
\(~~~~\) 那考虑对于已经合法的 \(\{t_i\}\) ,其他球就可以乱放了,所以每组 \(\{t_i\}\) 对应 \(n^{k-n}\) 种方案,每种的贡献为 \(1\) 。
\(~~~~\) 所以最后的答案就是 \(\dfrac{k!}{(k-n)!}\times n^{k-n}\) ,然后除上总方案数就是期望。
\(~~~~\) 现在加上初始值,那我们认为每个盒子的增量为 \(b_i\) ,依然很好求总方案,那要求的最后的总权值期望是:
\(~~~~\) 把里面拆开可以发现总是由 \(x\) 项 \(a\) 的积和 \(n-x\) 项 \(b\) 的积做乘法,然后加和。
\(~~~~\) 那就预处理出所有由 \(x\) 项 \(a\) 相乘得到的数之和 \(\text {and}\) 所有 \(n-x\) 项 \(b\) 相乘的得到的数之和,其中 \(a\) 这部分记为 \(f\) ,可以有 DP:定义 \(f_{i,j}\) 表示前 \(i\) 个数中所有选 \(j\) 个情况的积的和,转移方程:\(f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\times a_i\) 。而求 \(g\) 的话只需要类比上面就可以了,答案是 \(\dfrac{k!}{(k-i)!}\times n^{k-i}\) 。
\(~~~~\) 那最后的答案由 \(f\) 和 \(g\) 卷一下就可以得到:
\(~~~~\) 然后再除以方案数就是期望了。
代码
查看代码
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int MOD=998244353;
int n,k;
int arr[1005],dp[1005][1005],f[1005],g[1005];
ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1) ret=ret*a%MOD;
b>>=1;a=a*a%MOD;
}
return ret;
}
int Get(int x)
{
int ret=1;
for(int i=k;i>=k-x+1;i--) ret=1ll*ret*i%MOD;
return ret;
}
int main() {
scanf("%d %d",&n,&k);
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
for(int j=0;j<=n;j++)
dp[i][j]=(dp[i-1][j]+(j>=1?1ll*dp[i-1][j-1]*arr[i]%MOD:0))%MOD;
if(i==n) for(int j=0;j<=n;j++) f[j]=dp[i][j];
}
int Up=min(n,k);
for(int i=0;i<=Up;i++)
g[i]=1ll*Get(i)*qpow(n,k-i)%MOD;
int Ans=0;
for(int i=0;i<=n;i++) Ans=(1ll*Ans+1ll*f[i]*g[n-i]%MOD)%MOD;
printf("%lld",1ll*Ans*qpow(qpow(n,k),MOD-2)%MOD);
return 0;
}