AtCoder Beginner Contest 231 G - Balls in Boxes 简要题解
提供一个生成函数的推法。
考虑把最后的答案写出来:
\sum_{c_1+\cdots+c_n=k}\frac{k!}{n^k\prod_{i=1}^nc_i!}\prod_{i=1}^n(A_i+c_i)
这个式子的意思是把可能的一种增加方案(c_1,\cdots,c_n)枚举出来,算出达到这种方案的情况数(\frac{k!}{\prod c_i!}),再算出概率和这种方案的贡献。
观察这个式子,和第i个元素相关的部分为\frac{A_i+c_i}{c_i!},把这东西写成EGF的形式就是F_i(x)=e^x(A_i+x),所以答案也就变成了:
[x^k]\frac{k!}{n^k}\prod_{i=1}^n e^x(A_i+x)=[x^k]\frac{k!}{n^k}e^{nx}\prod_{i=1}^n(A_i+x)
乘积部分是一个n次多项式,不需要分治ntt可以直接暴力算,又众所周知e^{nx}=\sum_{i=0}^{\infty} \frac{(nx)^i}{i!},把对应项乘起来就可以了。
namespace My_Math{
#define N 100000
int fac[N+100],invfac[N+100];
int add(int x,int y) {return x+y>=maxd?x+y-maxd:x+y;}
int dec(int x,int y) {return x<y?x-y+maxd:x-y;}
int mul(int x,int y) {return 1ll*x*y%maxd;}
ll qpow(ll x,int y)
{
ll ans=1;
while (y)
{
if (y&1) ans=mul(ans,x);
x=mul(x,x);y>>=1;
}
return ans;
}
int getinv(int x) {return qpow(x,maxd-2);}
int C(int n,int m)
{
if ((n<m) || (n<0) || (m<0)) return 0;
return mul(mul(fac[n],invfac[m]),invfac[n-m]);
}
void math_init()
{
fac[0]=invfac[0]=1;
rep(i,1,N) fac[i]=mul(fac[i-1],i);
invfac[N]=getinv(fac[N]);
per(i,N-1,1) invfac[i]=mul(invfac[i+1],i+1);
}
#undef N
}
using namespace My_Math;
int n,k,f[N][N];
int main()
{
f[0][0]=1;
n=read();k=read();
rep(i,1,n)
{
int a=read();
f[i][0]=mul(f[i-1][0],a);
rep(j,1,i) f[i][j]=add(f[i-1][j-1],mul(f[i-1][j],a));
}
int ans=0,prod=1,inv=getinv(qpow(n,k));
rep(i,0,min(k,n))
{
int tmp=mul(prod,qpow(n,k-i));
tmp=mul(tmp,inv);
ans=add(ans,mul(tmp,f[n][i]));
prod=mul(prod,k-i);
}
printf("%d\n",ans);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步