CF891E Lust
生成函数,期望
题意
你有 n 个数 a1,a2,…,an 要进行 k 次操作,每次随机选择一个数 x∈[1,n],把 ax 减一,并将答案增加除 ax 外所有数的乘积。
求最终答案的期望,答案对 109+7 取模。
1≤n≤5000,1≤k≤109,0≤ai≤109
题解
可以发现,答案的增加量等于 ∏ai 的减少量。设最后得到的序列为 {bi} ,那么答案为:
i=1∏nai−i=1∏n(ai−bi)
右边,求期望值
E=nk1⎝⎛∑bi=k∑∏bi!k!i=1∏n(ai−bi)⎠⎞=nkk!∑bi=k∑i=1∏nbi!ai−bi
含义:nk 表示操作序列总数,显然每种情况概率是一样的故 nk1 ,右边便是计算所有情况的总和,先是枚举 {bi} 满足 ∑bi=k ,对于这组确定的 {bi} 显然对应 ∏bi!k! 个操作序列(熟知的多重集排列数),然后 ∏i=1n(ai−bi) 对应贡献。
然后就是要算 ∑bi=k∑i=1∏nbi!ai−bi ,这坨东西构造生成函数:
fi^(x)=j=0∑∞j!ai−jxj=j=0∑∞j!aixj−j=1∑∞(j−1)!xxj−1=(ai−x)ex
整个式子:
F^(x)=i=1∏nfi^(x)=enxi=1∏n(ai−x)
令 G(x)=∏i=1n(ai−x) ,可以方便得求出所有系数 gp=[xp]G(x) 。
我们要求的是 [xk]F^(x) :
[xk]F^(x)=[xk]G(x)∗enx=j=0∑kgj(k−j)!nk−j
然后 j 枚举其实到 n 就够了,G(x) 只有 n 项。可得答案:
E=nkk!j=0∑ngj(k−j)!nk−j=j=0∑nkjnjgj
CODE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <bits/stdc++.h> template<typename _Tp> inline void read(_Tp &x) { x=0;bool fg=0;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') fg^=1;ch=getchar();} while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(_Tp)(ch^48),ch=getchar(); if(fg) x=-x; } template<typename _Tp,typename... Args>void read(_Tp &t,Args &... args){read(t);read(args...);} using namespace std; typedef long long ll; const int mod = 1e9+7; const int N = 5005; int n,k,a[N]; ll g[2][N],invn; ll fpow(ll a,ll b=mod-2) { ll r=1; for(;b;b>>=1,a=a*a%mod) if(b&1) r=r*a%mod; return r; } template<typename Tp> void Add(Tp &x, Tp y) {x=(x+y)%mod;} template<typename Tp> void Mul(Tp &x, Tp y) {x=(x*y)%mod;}
int main() { read(n,k); ll ans=1,pp=1; for(int i=1;i<=n;++i) read(a[i]),Mul(ans,(ll)a[i]); g[0][0]=1; bool V=0; invn=fpow(n); for(int i=1;i<=n;++i) { memset(g[V^1],0,sizeof(g[V^1])); for(int j=0;j<i;++j) { g[V^1][j+1]=(mod-g[V][j]); Add(g[V^1][j],g[V][j]*a[i]%mod); } V^=1; } for(int j=0;j<=n;++j) { Add(ans,mod-pp*g[V][j]%mod); Mul(pp,(ll)invn*(k-j)%mod); } printf("%lld\n",ans); return 0; }
|