CF932E: Team Work 题解(第二类斯特林数)

CF932E: Team Work 题解

E:

题意:输入 n,k, 求 i=1nCni·ik。(n<=1e9,k<=5000)

非常简短的题面,但是威力惊人啊,数学不好根本不知道如何下手,推着推着式子发现越推越复杂。

Solution1:求导

我们先把我们以前会的东西列出来:

①:i=0nCni=2n

②:i=1nCni=2n1

③:i=1nCni·xi=(1+x)n1(二项式展开)

④:i=1nCni·i=n·2n1

第四个式子左边是什么呢,就是 n 个数任意选,把每种情况选中的个数加起来。因为每个位置对总和贡献相同,都在 2n1 种情况中出现过,因此总和为 n·2n1

但这道题要加的不是 i,而是 ikk 是一个不变的数字,和③式形式不是很一样(其实是很不一样)。

这时候要用到我们的求导大法,将③式左右同时对 x 求导,得⑤式:

⑤:i=1nCni·ixi1=n(1+x)n1

为什么下标 i 要从 1 开始,因为 x0 求导完是 0 而不是 x1 ,我们避免这种讨论,所以让多项式指数一直大于 0。

然后你会发现④式其实就是⑤式 x=1 的情况。我们有了一个思路:通过求导,使得左边的系数变成 ik ,这里的 ik 都与 x 无关,我们最终代入 x=1 便可以消去 x

为了再次求导产生 i ,我们将两边乘上 x,左式变为 i=1nCni·ixi。这样不断地求导再乘 x,最终可推出我们想求的式子。

i=1nCni·xi=(1+x)n1

i=1nCni·i·xi=nx(1+x)n1

i=1nCni·i2·xi=nx(1+x)n1+n(n1)x2(1+x)n2

......

i=1nCni·ik·xi=a1x(1+x)n1+a2x2(1+x)n2+a3x3(1+x)n3+...+akxk(1+x)nk

i=1nCni·ik=a12n1+a22n2+a32n3+...+ak2nk

我们用自己的猴力计算,把右边求导再乘 x,发现每一项的系数是可以递推求出的 : ai,j=ai1,j1(n+1j)+ai1,jj。复杂度 k2

最后还要注意到当 n<k 时,右式不足以以这种形式导 k 次,所以 n 比较小的时候直接暴力求就行。

const ll p=1000000007; ll T; ll n,k; ll ans,a[2][N]; ll f[N],ni[N]; 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; } inline ll C(ll aa,ll bb) { return f[aa] * ni[bb] %p * ni[aa-bb] %p; } void qiu() { f[0] = ni[0] = 1; for(ll i=1;i<=N-10;i++) f[i] = f[i-1] * i %p; ni[N-10] = ksm(f[N-10],p-2); for(ll i=N-11;i>=1;i--) ni[i] = ni[i+1] * (i+1) %p; } int main() { qiu(); n = read(); k = read(); if(n<=k+10) { for(ll i=1;i<=n;i++) (ans += C(n,i) * ksm(i,k) %p) %= p; cout<<(ans%p+p)%p; return 0; } a[1][1] = n; for(ll i=2;i<=k;i++) { ll cl = i&1; for(ll j=1;j<=i;j++) a[cl][j] = (a[cl^1][j-1] * (n+1-j) %p + a[cl^1][j] * j %p) %p; } for(ll i=1;i<=k;i++) (ans += a[k&1][i] * ksm(2,n-i) %p) %= p; cout<<(ans%p+p)%p; return 0; }

突然发现这题不就是2020省选D1T2组合数问题吗,数据范围也是k^2,当时同省很多大佬都会做的,退役是完全因为自己菜,只能说相见恨晚。


Solution2:第二类斯特林数

此题可以用斯特林数进行推导,可惜模数不能NTT,因此也只好用 n 方递推求斯特林数。

事实上在上面求系数 a 时的递推式就是斯特林数乘上一个下降幂。


先来个纯数学推法:

由小球放盒子可以得到一个拆解 ik 的公式:见此博客二级标题“经典应用”

ik=j=1kSkjCij·j!

枚举上界到 k 或到 i 其实是一样的,因为后面的两个括号暗示了 j<=min(k,i),否则为 0,因为这题 k 比 i 上限要小,为了方便后续推导我们写成 k。

i=0nCni·ik

=i=0nCnij=1kSkjCij·j!

=j=1kSkj·j!i=0nCniCij

=j=1kSkj·j!i=0nn!i!(ni)!·i!j!(ij)!

=j=1kSkji=0nn!(ni)!(ij)!

n 太大没法求后面的和,但是一个类似组合数求和的形式让我们想到二项式,我们往二项式去凑,希望可以把组合数求和化为幂次的形式.

而将右边化为组合数就需要有 (A+B)!A!B! 的形式,我们发现下面两项加起来刚好能把 i 消掉,很妙。

=j=1kSkji=0n(nj)!(ni)!(ij)!·n!(nj)!

=j=1kSkj·n!(nj)!i=0nCnjij

右边求和虽然长得丑但也是二项式求和的形式,ij<0 时组合数的值应当为 0。

=j=1kSkj·n!(nj)!2nj

本来可以 klogk 求 第二类斯特林数(行),可惜模数不喜欢。

Solution3:还是第二类斯特林数

litble佬的题解

担心上面的推导过程写错,或者卡住?litble佬给出了一个省去好多步骤的推法,但是她的描述有点太简略了,我详细写下。

转化题意,题目相当于:从 n 个盒子里选 i 个,然后将 k 个球任意装进选择的 i 个盒子中(可为空),求总方案数。

我们也枚举盒子,但是我们不枚举选择的 i 个,而是枚举装了球的盒子数量 j (也就是不可为空)。

我们考虑有多少种情况是 j 个盒子装了球:Cnj 先选 j 个盒子,其他的 n-j 个盒子是否被选进那 i 个当中无所谓,因此一共有 2nj 种情况满足:这 j 个盒子装了球。

答案为:j=1kF(k,j)·Cnj·2njF(k,j) 表示 k 个球装进 j 个盒子,每个盒子不为空的方案,显然 F(k,j)=Skj·j!

所以最终答案为:

j=1kSkj·j!·Cnj·2nj

=j=1kSkj·n!(nj)!·2nj

=j=1kSkj·nj_·2nj

虽然 n 很大没法求阶乘,但是转化为下降幂的形式后,因为指数 j 上限为 k,因此可以暴力乘出结果。


__EOF__

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