zoukankan      html  css  js  c++  java
  • 伯努利数求自然数幂和

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

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

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

    伯努利的结果是:

    [S_t(n) = frac{1}{t+1} sum_{i=0}^{t} inom{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} inom{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} inom{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} inom{n}{i} f_i g_{n-i} ]

    我们可以对开始那个递推式使用指数生成函数:我们用(n)代替(t+1),然后在两侧加上(B_n)得到

    [sum_{i=0}^{n} inom{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\%)的数据,(nle 10^{13},mle 50)

    题解

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

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

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

    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
    ",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;
    }
    
  • 相关阅读:
    8.10
    今日头条笔试题 1~n的每个数,按字典序排完序后,第m个数是什么?
    Gym 100500B Conference Room(最小表示法,哈希)
    CodeForces 438D The Child and Sequence(线段树)
    UVALIVE 6905 Two Yachts(最小费用最大流)
    Gym Conference Room (最小表示法,哈希)
    hdu 2389 Rain on your Parade(二分图HK算法)
    Codeforces Fox And Dinner(最大流)
    zoj 3367 Counterfeit Money(dp)
    ZOJ3370. Radio Waves(2-sat)
  • 原文地址:https://www.cnblogs.com/autoint/p/power_sum_of_natural_numbers.html
Copyright © 2011-2022 走看看