CF891E: Lust 题解(指数生成函数)

E:

题意:给你序列 a,进行 k 次操作,每次随机选择一个 x,使得 s+=i=1nai(i!=x)ai=1,求 k 次后 s 期望值。(n<=5000, k<=1e9)


Solution:

首先转化下题意,我们把序列 a 看做是一个 n 维的超立方体,每次的操作相当于随机选择一维,切下来一个厚度为1的面包片(比喻),s 加上的值就是面包片的体积,ai 减去 1 相当于这一维的厚度减少了 1 ,因为我们把面包片拿走了。那么就很好推出:s=i=1naii=1n(aibi)bi 为第 i 个位置被选择的次数。

前一项是初始体积,我们求一下切 k 次后的期望体积 E=i=1n(aibi) 。隐约感觉出这题是生成函数,因此我们推式子时尽量往那里靠。

我们一共有 nk 种随机操作方式,发现每一种不同的 b 数列对应一种不同的贡献,设 f(b1,b2,...,bn) 表示 b 数列这样分布的次数,其中 bi=k

由排列组合的知识可知 f(b1,b2,...,bn)=k!b1!b2!...bn!

每一种 b 数列的贡献是 i=1n(aibi),则: E=1nkBf(b1,b2,...,bn)i=1n(aibi)。(B 是指把所有 b 数列情况加起来)

想求这个式子,就需要搬出我们的指数生成函数 (EGF)。

因为 aibi 是连乘的形式,我们可以放在指数生成函数中系数的位置上,表示选择每个 bi 值造成的贡献。

F(x)i 表示第 i 个位置的生成函数:

F(x)i=(ai0)+(ai1)x11!+(ai2)x22!+...+(ain)xnn!

xk 的系数相当于 bi=k 时的贡献。

F(x)i=j=0n(aij)xjj!

F(x)i=j=0n(aixjj!xj1(j1)!·x)

F(x)i=j=0naixjj!j=1nxj1(j1)!·x

F(x)i=j=0naixjj!j=0nxjj!·x

F(x)i=j=0n(aix)xjj!

F(x)i=(aix)ex

总生成函数:F(x)=i=1nF(x)i=enxi=1n(aix),这个式子助于我们计算。

pi 为多项式 i=1n(aix) 的系数,我们知道 enx=i=0nixii!,所以 F(x)xk 项为: [xk]F(x)=i=0kpinki(ki)!

同时,表示 F(x) 意义的式子为: F(x)=k=0(B1b1!b2!...bn!i=1n(aibi))xk

我们要求的期望为:

E=1nkBf(b1,b2,...,bn)i=1n(aibi)

E=1nkBk!b1!b2!...bn!i=1n(aibi)

发现就是 F(x)xk 项系数乘上常数 k!nk

E=[xk]F(x)·k!nk

E=i=0kpinki(ki)!·k!nk

E=i=0kpiki_ni

k 太大,还是不会求?其实 pi 只有 n 项,后面的项全是 0。(如果你推 F(x) 系数时反了过来:i=0kpkinii!,那么你应该注意到 i>kn 时才有意义)

所以最终答案为 s=i=1naii=0npiki_ni

计算多项式 i=1n(aix) 可以用多项式分治乘,从 n2 优化至 nlog2n,但是这题模数不是998244353,FFT又怕掉精度,所以直接写暴力了。

#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define FOR() ll le=e[u].size();for(ll i=0;i<le;i++) #define QWQ cout<<"QwQ\n"; #define ll long long #include <complex> #include <queue> #include <map> #define ls now<<1 #define rs now<<1|1 using namespace std; const ll N=501010; const ll qwq=303030; const ll inf=0x3f3f3f3f; const ll p=1000000007; inline ll read() { ll sum = 0, ff = 1; char c = getchar(); while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); } while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * ff; } ll n,k; ll a[N],b[2][N]; ll S=1; inline ll ksm(ll aa,ll bb) { ll sum = 1; while(bb) { if(bb&1) sum = sum * aa %p; bb >>= 1; aa = aa * aa %p; } return sum; } int main() { n = read(); k = read(); for(ll i=1;i<=n;i++) a[i] = read(), S = S * a[i] %p; b[0][0] = 1; for(int i=1;i<=n;i++) { int cl = i&1; b[cl][0] = b[cl^1][0] * a[i] %p; for(int j=1;j<=n;j++) { b[cl][j] = (a[i] * b[cl^1][j] - b[cl^1][j-1]) %p; } } ll mi = 1; for(int i=0;i<=n;i++) { (S -= b[n&1][i] * ksm(n,i*(p-2)) %p * mi %p - p) %= p; mi = mi * (k-i) %p; } cout<<(S%p+p)%p; return 0; }

__EOF__

本文作者枫叶晴
本文链接https://www.cnblogs.com/maple276/p/18055641.html
关于博主:菜菜菜
版权声明:呃呃呃
声援博主:呐呐呐
posted @   maple276  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示