伯努利数求自然数幂和

伯努利数(指数生成函数)

如果我们用别的算法算出对于不同的\(t\)的自然数幂和的多项式,然后写在一张纸上,其实可以观察到一些性质(这里就不列出来了)。雅各布·伯努利就发现了其中的规律…

这里为了方便,我们修改一下我们的记号:\(S_t(n) = \sum_{i=0}^{n-1} i^t\)。我们把\(n^t\)的一项去掉了。这会比较方便接下来的分析,并且影响不大,就像我们在斯特林数那一节所做的一样。

伯努利的结果是:
\[ S_t(n) = \frac{1}{t+1} \sum_{i=0}^{t} \binom{t+1}{i}B_i n^{t+1-i} \]
其中\(B_i\)是伯努利数。这个式子的证明我们先放一放,先看看它有什么用。

注意到,当\(n=1\)时,\(S_t(n)\)就变成了\(0^t\),所以只有\(t=0\)时它才是\(1\),其它时候都是\(0\)。注意到在这个时候,我们得到了一个一侧含有伯努利数的式子:
\[ \sum_{i=0}^{t} \binom{t+1}{i} B_i = 0,t>0 \]
而如果\(t=0\),我们可以得到\(B_0=1\)。现在我们就可以递推算\(B_t\)了,移项即可得到递推式,复杂度是\(O(t^2)\)的。
\[ B_t = -\frac{1}{t+1} \sum_{i=0}^{t-1} \binom{t+1}{i} B_i \]
而且注意到上面伯努利得到的结论直接就是一个多项式的形式,所以通过伯努利数得到多项式是\(O(t)\)的,是这些方法里最快的了。

接下来我们需要证明这个东西…这个东西可以用归纳法证明,但是看起来非常复杂暴力…好在还有一种方法是指数生成函数。

一个数列\(f_n\)的指数生成函数是$\hat F(z) = \sum_{n \geq 0} f_n \frac{z^n}{n!} $。

所以如果我们把两个指数生成函数相乘,得到的指数生成函数是它们的二项卷积的指数生成函数。比如对于\(\hat F(z) \hat G(z) = \hat H(z)\)
\[ h_n = \sum_{i=0}^{n} \binom{n}{i} f_i g_{n-i} \]

我们可以对开始那个递推式使用指数生成函数:我们用\(n\)代替\(t+1\),然后在两侧加上\(B_n\)得到
\[ \sum_{i=0}^{n} \binom{n}{i} B_i = B_n,n>1 \]

左侧其实是\(\hat B(z)\)与一个系数全是\(1\)的数列的指数生成函数(\(e^z\))二项卷积,同时我们补上\(n=1\)的情况,此时在右边会多一个\(1\)。所以
\[ \hat B(z) e^z = \hat B(z) + z \]

变形就能得到\(\hat B(z) = \frac{z}{e^z-1}\),这就是伯努利数的指数生成函数。(这也得出了一种通过FFT对多项式求逆的一种\(O(t \log t)\)的预处理伯努利数的方法)

接下来考虑\(t\)次方和。我们尝试利用指数生成函数凑\(t\)次方和。因为普通生成函数下我们得到的是分母上的等差数列,只能写成调和数的形式,所以实际意义不大。而指数生成函数中我们有希望把等差数列挪到指数上,从而得到一个等比数列,然后就可以化简了。
\[ e^{kz} = \sum_{i \geq 0} k^i \frac{z^i}{i!} \]

这就是数列\(k^0,k^1,k^2,\cdots\)的指数生成函数,所以如果我们要求自然数幂和,我们只要把\(0\)\(n-1\)\(e^{kz}\)相加:
\[ \sum_{k=0}^{n-1} e^{kz} = \frac{e^{nz}-1}{e^z-1} \]
这就得到了一个简单的形式了。联系伯努利数的生成函数就有:
\[ \hat S_t(z) = \hat B(z) \frac{e^{nz}-1}{z} \]
右侧又可以变成类似两个数列的二项卷积的形式,不过有一点不同,其中\(\frac{e^{nz}-1}{z}\)其实就是把\(n^0,n^1,n^2,\cdots\)的生成函数去掉常数项再在普通生成函数意义下挪了一位。这就导致了公式的二项式系数里面那个奇怪的\(+1\)。展开就是我们的公式了。

[TJOI2018]教科书般的亵渎

小豆喜欢玩游戏,现在他在玩一个游戏遇到这样的场面,每个怪的血量为\(a_i\),且每个怪物血量均不相同,小豆手里有无限张“亵渎”。亵渎的效果是对所有的怪造成\(1\)点伤害,如果有怪死亡,则再次施放该法术。我们认为血量为\(0\)怪物死亡。

小豆使用一张 “亵渎”会获得一定的分数,分数计算如下,在使用一张“亵渎”之后,每一个被亵渎造成伤害的怪会产生\(x^k\),其中\(x\)是造成伤害前怪的血量为\(x\)和需要杀死所有怪物所需的“亵渎”的张数\(k\)

输出小豆的最后可以获得的分数\(\mod 10^9+7\)

【输入格式】
每组组测试数据第一行为\(n,m\),表示有当前怪物最高的血量\(n\),和\(m\)种没有出现的血量。

接下来\(m\)行,每行\(1\)个数\(a_i\),表示场上没有血量为\(a_i\)的怪物。

【数据范围】
对于\(100\%\)的数据,\(n\le 10^{13},m\le 50\)

题解

每个没有出现的血量都会打断一次“亵渎”,所以需要杀死所有怪物所需的“亵渎”的张数\(k=m+1\)

那么每次的贡献是一个自然数幂和-没有出现的数的幂和的形式,所以问题转化成了求自然数幂和。

用伯努利数解决,时间复杂度\(O(m^2)\)

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read(){
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;
}
template<class T>il T read(T&x){
    return x=read<T>();
}
using namespace std;
typedef long long LL;

co int mod=1000000000+7;
il int add(int a,int b){
    return (a+=b)>=mod?a-mod:a;
}
il int mul(int a,int b){
    return (LL)a*b%mod;
}
il int fpow(int a,int b){
    int ans=1;
    for(;b;b>>=1,a=mul(a,a))
        if(b&1) ans=mul(ans,a);
    return ans;
}

co int N=60;
LL n,a[N];
int m,b[N],c[N][N],inv[N];

int calc(LL n){
    int ans=0;
    for(int i=1;i<=m+1;++i)
        ans=add(ans,mul(c[m+1][i],mul(b[m+1-i],fpow((n+1)%mod,i))));
    ans=mul(ans,inv[m+1]);
    return ans;
}
void real_main(){
    read(n),read(m);
    for(int i=1;i<=m;++i) read(a[i]);
    a[++m]=++n;
    sort(a+1,a+m+1);
    int ans=0;
    for(int i=1;i<=m;++i){
        for(int j=i;j<=m;++j)
            ans=add(ans,add(calc(a[j]-1),mod-calc(a[j-1])));
        for(int j=i+1;j<=m;++j) a[j]-=a[i];
        a[i]=0;
    }
    printf("%d\n",ans);
}
int main(){
    inv[0]=inv[1]=1;
    for(int i=2;i<=55;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
    for(int i=0;i<=55;++i){
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;++j) c[i][j]=add(c[i-1][j-1],c[i-1][j]);
    }
    b[0]=1;
    for(int i=1;i<=55;++i){
        for(int j=0;j<i;++j) b[i]=add(b[i],mul(c[i+1][j],b[j]));
        b[i]=mul(mod-b[i],inv[i+1]);
    }
    for(int t=read<int>();t--;) real_main();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/power_sum_of_natural_numbers.html