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;
    }
    
  • 相关阅读:
    智能佳 金刚足球机器人 竞赛机器人 智能机器人
    DIY小能手|别买电动滑板车了,咱做一台吧
    !!2016/02/22——当日买入——事后追悔,总结经验,忘记了买票的初衷!
    20160222深夜 支撑与阻力的问题,突破要不要买,回踩要不要接
    2016/2/4——昨天操作错误
    C语言 · 瓷砖铺放
    C语言 · 字符串编辑
    C语言 · 比较字符串
    C语言 · 3000米排名预测
    C语言 · 陶陶摘苹果2
  • 原文地址:https://www.cnblogs.com/autoint/p/power_sum_of_natural_numbers.html
Copyright © 2011-2022 走看看