[生成函数阶段性小结][CF891E]Lust
2018-01-11 22:31
337 查看
问题描述
给你一个长度为n的数组a[],还有操作数K,每次操作你在下标[1..n]中等概率选择一个下标x,贡献+=∏i!=xa[i],然后a[x]-=1。求K次操作后贡献期望值,对1e9+7取模。n<=5000,K<=1e9
问题分析
首先期望可以看作是所有方案的贡献除以方案数。我们看到那个∏不能算x,这不利于我们化式子,直接这样根本没有思路。
我们稍微化一化:∏ia[i]−∏i!=xa′[i],其中a’为a[x]-=1后的序列。
我们观察连续几次相减,发现中间的项全部被消掉了。
那么设b[i]表示i这个位置被删了b[i]次。那么一种方案的贡献就是∏a[i]−∏(a[i]−b[i])。
那么E(贡献)=∏ai−K!∏(ai−bi)nk。
其中b[i]要枚举所有情况。K!表示,当b[]确定时,每次操作的x的顺序有K!。
那么问题变成了怎么把所有b[]的情况的贡献计算出来。
生成函数
一般生成函数
记A(x)=∑n≥0aixi为原序列a[]的一个生成函数,由于一般使用生成函数,我们是用来化简一些转移的,所以并不关心x的具体值是什么,我们只关注系数。就比如背包问题,把记录当前做到的每种体积的方案的数组f[]变成生成函数,我们和一个物品的生成函数相乘,即可得到转移过后的f[]的生成函数。
上面这种转移我们从多项式的角度理解,即卷积,写过FFT的人应该都能理解。
生成函数的笛卡尔积就是两个生成函数A和B的卷积。
生成函数的优美在于,可以把一些特殊的形式幂级数化为一个低次的式子.
比如A(x)=1+x+x2+...x+∞=11−x,这是等比数列求和,由于正无穷的项的系数我们不会用到,所以忽略了。
这样,几个多项式相乘的形式就可能很简单,也是我们做题的关键,即把答案的生成函数做出来,然后就可以展开得出系数,这道题就是一个应用。
几个重要的式子
OGF的:11−x=∑i≥0xi
11−axm=∑i≥0Cm−1i+m−1aixi
这个怎么理解呢?考虑一个背包问题,组合数的意义是i的体积可以由m种球共i个(体积为1)组成;a为某个系数,其实可以把a和x放在一起,就跟第一个式子一样了。
特别地,x(1−x)2=∑i≥1i∗xi
分子的x相当于把原序列右移一位。
EGF(指数型)的,也就是形如A(x)=∑i≥0aii!xi,实际操作的时候我们储存的系数是aii!
ex=∑i≥0xii!
ln(1+x)=∑i>0(−1)i+1xii
−ln(1−x)=∑i>0xii
记住就好。后面两个没有0可以理解为不能被0除。
十分脑洞地,我们上面的x可以代表一个多项式,那么就可以出现非常好玩的情况。
比如A(x)=∑i≥0xii!=ex,B(x)=∑i>0(−1)i+1xii=ln(1+x),那么A(x)B(x)=1+x。
EGF有一个模型是带标号的对象拼接,这个先不写了,还没做到。
有了这些知识,我们可以轻松解决这道题了!
回到题目
E(贡献)=∏ai−K!∏(ai−bi)nk我们只用算后面那一块。
首先算(ai-bi)那块。
我们先分析某个位置i,我们设生成函数Fi(x)代表a[i]减去某个b[i]的贡献,我们要表示他的全部情况,不妨设Fi(x)=∑j≥0ai−jj!xj,第j次的系数就是b[i]=j这一位的贡献。
推式子
Fi(x)=∑j≥0ai−jj!xj
=∑j≥0aij!xj−1(j−1)!xj
注意到ai是常数。
=(ai−x)ex
那么所有的贡献和就是所有生成函数乘起来。
Ftot=enx∏i(ai−x)。
分别考虑左右某一项的系数,左边是熟知的形式,右边可以暴力卷积,(像背包一样做)。设右边的第i项系数为c[i],那么答案变成∑i=0..nc[i]∗nK−ii!,i到n为止就行了,c只有0~n有东西。
带回去,∑i=0..nc[i]∗Ki↓ni,Ki↓表示∏j=K..K−i+1j。
那么答案就出来了。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<set> using namespace std; typedef long long ll; typedef double db; #define fo(i,j,k) for(i=j;i<=k;i++) #define fd(i,j,k) for(i=j;i>=k;i--) #define cmax(a,b) (a=(a>b)?a:b) #define cmin(a,b) (a=(a<b)?a:b) const int N=5e3+5,mo=1e9+7,M=2e5+5; int read() { int x=0; char ch=getchar(); while (ch<'0'||ch>'9') ch=getchar(); while (ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x; } int n,K,a ,F ,G ,rev ,fac ,ans,i,j,prod,In; int ksm(int x,int y) { int ret=1; while (y) { if (y&1) ret=1ll*ret*x%mo; y>>=1; x=1ll*x*x%mo; } return ret; } int dwm(int x,int y) { int ret=1; while (y--) { ret=1ll*ret*x%mo; x--; } return ret; } int main() { freopen("t4.in","r",stdin); //freopen("t4.out","w",stdout); scanf("%d %d",&n,&K); fo(i,1,n) scanf("%d\n",a+i); F[0]=1; fo(i,1,n) { fd(j,i,1) F[j]=(1ll*F[j]*a[i]-F[j-1])%mo; F[0]=1ll*F[0]*a[i]%mo; } In=1; fo(i,0,n) { ans=(ans+1ll*F[i]*dwm(K,i)%mo*ksm(In,mo-2))%mo; In=1ll*In*n%mo; } prod=1; fo(i,1,n) prod=1ll*prod*a[i]%mo; ans=(prod-ans)%mo; printf("%d\n",(ans+mo)%mo); }
相关文章推荐
- PHP GD库相关图像生成和处理函数小结
- redis练习手册<八>string主要操作函数小结
- 函数调用小结
- 生成函数入门题汇总
- Chromium界面分析小结(十)界面构造主要类和函数
- 生成N个节点随机二叉查找树的函数 具有从1到N的不同的关键字
- 自动生成拼音检索码 函数
- 在JIT编译生成的函数上设置断点
- 图论专题小结:最小生成树算法
- JavaScript生成随机数的4种自定义函数分享
- 编辑器自动生成的函数
- 条款06 若不想使用编译器自动生成的函数 就该明确拒绝
- [Python入门及进阶笔记]Python-基础-函数小结
- Python生成器结合列表生成式迭代输出每行杨辉三角函数生成的列表值
- 了解编译器默默生成哪些函数(Effective C++_5)
- 生成关键字是1~N的随机二叉查找树的函数
- Redis系列-存储篇set主要操作函数小结
- ABAP--一个不错的函数模块的文档生成程序
- Linux网络编程IPv4和IPv6的inet_addr、inet_aton、inet_pton等函数小结