O(m²) 实现等幂求和——伯努利数

等幂求和

伯努利数是以雅各布 伯努利的名字命名的,我们记

Sm(n)=k=0n1km

伯努利 暴力 算出了前几项,并进行了观察:

S0(n)=nS1(n)=12n212nS2(n)=13n312n2+16nS3(n)=14n412n3+14n2+0nS4(n)=15n512n4+13n3+0n2130nS5(n)=16n612n5+512n4+0n3112n2+0nS6(n)=17n712n6+12n5+0n416n3+0n2+142n

我们发现,在 Sm(n) 中,nm+1 的系数总是 1m+1nm 的系数是 12nm1 的系数是 m12nm2 的系数是 0nm3 的系数是 m(m1)(m2)720nm4 的系数是 0nmk 的系数总是某个常数乘上 mk_=m!(mk)!

递推公式

根据伯努利的发现,我们有

Sm(n)=1m+1k=0m(m+1k)Bknm+1k

其中 Bn 为伯努利数,其由一个递归关系定义:

k=0m(m+1k)Bk=[m=0]

便于计算的表示:

B0=1Bm=k=0m1(m+1k)Bkm+1,m>0

根据手算,不难得出前几个值:

n 0 1 2 3 4 5 6 7 8 9 10 11 12
Bn 1 12 16 0 130 0 142 0 130 0 566 0 6912730

证明:

首先令

S^m(n)=1m+1k=0m(m+1k)Bknm+1k

那么现在我们就是要证明 Sm(n)=S^m(n),考虑使用数学归纳法。

首先

S0(n)=i=0n1i0=nS^0(n)=11(10)B0n1=n

Sm+1(n)+nm+1=k=0nkm+1=k=0n1(k+1)m+1=k=0n1j=0m+1(m+1j)kj=j=0m+1(m+1j)k=0n1kj=j=0m+1(m+1j)Sj(n)

m1 时,假设 i[0,m) 均有 Si(n)=S^i(n),将 Sm+1(n) 移项:

nm+1=j=0m+1(m+1j)Sj(n)Sm+1(n)=j=0m(m+1j)Sj(n)=j=0m1(m+1j)Sj(n)+(m+1m)Sm(n)=j=0m1(m+1j)S^j(n)+(m+1)Sm(n)

Δ=Sm(n)S^m(n)

那么现在就是要证 Δ=0

nm+1=[j=0m1(m+1j)S^j(n)+(m+1)S^m(n)]+(m+1)Sm(n)(m+1)S^m(n)=j=0m(m+1j)S^j(n)+(m+1)Δ

根据 S^m(n) 的定义将其展开,有

nm+1=j=0m(m+1j)1j+1k=0j(j+1k)Bknj+1k+(m+1)Δ

k 改为倒序枚举 并 利用 对称恒等式

(nm)=(nnm),nN,kZ

nm+1=j=0m(m+1j)1j+1k=0j(j+1jk)Bjknk+1+(m+1)Δ=j=0m(m+1j)1j+1k=0j(j+1k+1)Bjknk+1+(m+1)Δ

利用 吸收恒等式

(rk)=rk(r1k1),kZ,k0

nm+1=j=0m(m+1j)1j+1k=0jj+1k+1(jk)Bjknk+1+(m+1)Δ=j=0m(m+1j)k=0jnk+1k+1(jk)Bjk+(m+1)Δ=k=0mnk+1k+1j=km(m+1j)(jk)Bjk+(m+1)Δ

利用 三项式版恒等式

(rm)(mk)=(rk)(rkmk)

nm+1=k=0mnk+1k+1j=km(m+1k)(mk+1jk)Bjk+(m+1)Δ=k=0mnk+1k+1(m+1k)j=km(mk+1jk)Bjk+(m+1)Δ

jk 改为 j

nm+1=k=0mnk+1k+1(m+1k)j=0mk(mk+1j)Bj+(m+1)Δ

又根据伯努利数的递归定义

k=0m(m+1k)Bk=[m=0]

nm+1=k=0mnk+1k+1(m+1k)[mk=0]+(m+1)Δ=nm+1m+1(m+1m)+(m+1)Δ=nm+1+(m+1)Δ

至此,我们推出了

(m+1)Δ=0m+10Δ=0

证毕。

当然,还有一种用 指数生成函数 的更简单的证明方法,就先不证了(

代码实现

显然这东西一般用在有模数的情况下。

下列代码计算的是 k=0nkm,因此需要 n++;

时间复杂度为 O(m2)

暴力计算的时间为 O(nlogm),在 n 巨大时伯努利数有巨大优势,参见 P6271 [湖北省队互测2014]一个人的数论 使用莫反 + 伯努利数或时间复杂度同级的拉格朗日插值。

//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;

const int MAXN = 1e4 + 5;
const int N = 1e4;
const int MOD = 1e9 + 7;

int add(int a, int b) {return (a + b) % MOD;}
int mul(int a, int b) {return (ll)a * b % MOD;}

int inv[MAXN], C[MAXN][MAXN], B[MAXN];

void init()
{
	for (int i = 0; i <= N + 1; i++)
	{
		C[i][0] = C[i][i] = 1;
	}
	for (int i = 1; i <= N + 1; i++)
	{
		for (int j = 1; j < i; j++)
		{
			C[i][j] = add(C[i - 1][j], C[i - 1][j - 1]);
		}
	}
	
	inv[1] = 1;
	for (int i = 2; i <= N + 1; i++)
	{
		inv[i] = mul(MOD - MOD / i, inv[MOD % i]);
	}
	
	B[0] = 1;
	for (int m = 1; m <= N; m++)
	{
		int sum = 0;
		for (int k = 0; k < m; k++)
		{
			sum = add(sum, mul(C[m + 1][k], B[k]));
		}
		B[m] = mul(MOD - sum, inv[m + 1]);
	}
}

int qpow(int a, int b)
{
	int base = a, ans = 1;
	while (b)
	{
		if (b & 1)
		{
			ans = mul(ans, base);
		}
		base = mul(base, base);
		b >>= 1;
	}
	return ans;
}

int main()
{
	init(); // 计算伯努利数 
	int n, m;
	scanf("%d%d", &n, &m);
	n++;
	int res = 0;
	for (int k = 0; k <= m; k++)
	{
		res += mul(mul(C[m + 1][k], B[k]), qpow(n, m + 1 - k));
	}
	printf("%d\n", mul(inv[m + 1], res));
	return 0;
}

参考资料

  • [1] 伯努利数. OI-wiki
  • [2] Concrete Mathematics 5.1 BASIC IDENTITIES, 6.5 BERNOULLI NUMBER. Ronald L. Graham, Donald E. Knuth, Oren Patashnik.
posted @   mango09  阅读(222)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
-->
点击右上角即可分享
微信分享提示