伯努利數學習筆記

1.定義式

定義伯努利數列\(B_n\)滿足:

\[B_0=1,\sum_{i=0}^n{n+1\choose i}B_i=0(n>0) \]

2.遞推式

可以發現定義式裏面包含了\(B_n\)這一項,於是把\(B_n\)提出來:

\[-{n+1\choose n}B_n=\sum_{i=0}^{n-1}{n+1\choose i}B_i \\-(n+1)B_n=\sum_{i=0}^{n-1}{n+1\choose i}B_i \\B_n=-\frac{1}{n+1}\sum_{i=0}^{n-1}{n+1\choose i}B_i \]

直接用定義式求是\(O(n^2)\)的複雜度

3.生成函數

把定義式的循環上界減一,得:

\[\sum_{i=0}^{n-1}{n\choose i}B_i=0 \]

注意到組合數上標變成了\(n\),再加個\(B_n\)

\[\sum_{i=0}^{n-1}{n\choose i}B_i+B_n=B_n \\\sum_{i=0}^{n-1}{n\choose i}B_i+{n\choose n}B_n=B_n \\\sum_{i=0}^{n}{n\choose i}B_i=B_n \]

組合數很煩,把它拆開來:

\[\sum_{i=0}^{n}\frac{n!}{i!(n-i)!}B_i=B_n \\\sum_{i=0}^n\frac{B_i}{i!}\frac{1}{(n-i)!}=\frac{B_n}{n!} \]

兩邊都寫成生成函數的形式:

\[\sum_{n=0}(\sum_{i=0}^n\frac{B_i}{i!}\frac{1}{(n-i)!})x^n =\sum_{n=0}(\frac{B_n}{n!})x^n \]

設伯努利數的指數型生成函數爲\(B(x)=\sum_{n=0}\frac{B_n}{n!}x^n\),那麼左邊顯然就是\(B(x)e^x\),右邊就是\(B(x)\)
但是細想卻不對勁,如果\(B(x)e^x=B(x)\),那麼\(e^x=1\),顯然不成立。注意到一開始把循環上標減了1,而根據定義,伯努利數定義式的\(n\)必須大於\(0\),所以上面的式子只有當\(n-1>0\)時成立。
於是考慮列出\(B(x)e^x\)的第\(0,1\)項:

\[[x^n]B(x)e^x=\sum_{i=0}^n\frac{B_i}{i!}\frac{1}{(n-i)!} \\ [x^0]B(x)e^x=B_0 \\ [x^1]B(x)e^x=B_0+B_1 \]

\(B(x)\)的第一項只有\(B_1x\),少了個\(B_0x\),加上去即可:

\[B(x)e^x=B(x)+B_0x=B(x)+x \]

可以解出\(B(x)=\frac{x}{e^x-1}\)
注意到分母的第\(0\)次項爲\(0\),所以把分子除下去之後,第\(0\)次項不爲\(0\),可以用多項式求逆\(O(nlogn)\)做。

4.應用

說了這麼多,伯努利數有什麼用?
答:可以求解自然數冪和。
定義自然數冪和\(S(m,k)=\sum_{i=1}^mi^k\),直接求單個是\(O(nlogn)\)的(快速冪是\(O(logn)\))。使用伯努利數可以對於固定的\(m\)做到\(O(nlogn)\)求出前\(n\)個。
下面說下求法:
對於固定的\(m\),還是設\(S(m,k)\)的指數型生成函數爲

\[S_m(x)=\sum_{k=0}\frac{\sum_{i=1}^mi^k}{k!}x^k \]

交換求和符號得到:

\[S_m(x)=\sum_{i=1}^m\sum_{k=0}\frac{i^k}{k!}x^k \\=\sum_{i=1}^me^{ix}=\frac{e^{(m+1)x}-e^x}{e^x-1} \]

注意到分母和\(B(x)\)一樣,把\(B(x)\)帶進去:

\[S_m(x)=B(x)\frac{e^{(m+1)x}-e^x}{x}=B(x)\frac{e^x}{x}(e^{mx}-1) \]

\(e^{mx}\)展開得到:

\[S_m(x)=B(x)\frac{e^x}{x}(\sum_{i=0}\frac{(mx)^i}{i!}-1) \\=B(x)\frac{e^x}{x}\sum_{i=1}\frac{(mx)^i}{i!} \\=B(x)e^x\sum_{i=1}\frac{m^ix^{i-1}}{i!} \\=B(x)e^x\sum_{i=0}\frac{m^{i+1}x^i}{(i+1)!} \]

前面這個\(B(x)e^x\)不好處理,但注意到\(B(x)e^x=B(x)+x\),於是引入新的\(B'(x)=B(x)+x\),把\(B'(x)\)展開:

\[S_m(x)=(\sum_{i=0}\frac{B'_i}{i!}x^i)(\sum_{i=0}\frac{m^{i+1}}{(i+1)!}x^i) \\=\sum_{n=0}(\sum_{i=0}^n\frac{B'_i}{i!}\frac{m^{n-i+1}}{(n-i+1)!})x^n \\=\sum_{n=0}(\sum_{i=0}^n{n+1\choose i}B'_im^{n-i+1})\frac{x^n}{(n+1)!} \\=\sum_{n=0}(\frac{1}{n+1}\sum_{i=0}^n{n+1\choose i}B'_im^{n-i+1})\frac{x^n}{n!} \]

注意到\(S_m(x)=\sum_{k=0}S(m,k)\frac{x^k}{k!}\),所以:

\[S(m,k)=\frac{1}{k+1}\sum_{i=0}^k{k+1\choose i}B'_im^{k-i+1} \]

這個就可以稱爲自然數冪和的通項公式
注意到通項公式可以寫成卷積的形式:

\[S(m,k)=\frac{1}{k+1}\sum_{i=0}^k{k+1\choose i}B'_im^{k-i+1} \\=k!\sum_{i=0}^k\frac{B'_i}{i!}\frac{m^{k-i+1}}{(k-i+1)!} \]

因爲\(B'(x)=B(x)+x\),可以\(O(nlogn)\)求出每一項\(B'_n\),所以可以\(O(nlogn)\)求出\(m\)固定時\(k\in[1,n]\)\(S(m,k)\)

5.題目

luoguP3711倉鼠的數學題

已知\(a_0...a_n\),設\(S_k(x)=\sum_{i=0}^xi^k\),求出\(\sum_{k=0}^nS_k(x)a_k\),並輸出其每一項的係數,可以證明答案是個\(n+1\)次多項式。\(n\leq 250000\)\(0^0=1\),答案對\(998244353\)取模。

可以發現這裏的\(S_k(x)\)相比上面所說的多了個下標\(0\)。但是發現當\(k>0\)時,\(0^k=0\),所以\(S_k(x)=\sum_{i=1}^xi^k(k>0)\);而當\(k=0\)時,\(0^k=1\),所以\(S_k(x)=\sum_{i=1}^xi^k+1(k=0)\)
所以可以重新定義\(S_k(x)\)\(\sum_{i=1}^xi^k\),這樣答案的式子就是:

\[a_0+\sum_{k=0}^nS_k(x)a_k \]

只需要關注前面的求和式,用伯努利數將其展開得到:

\[\sum_{k=0}^n(k!\sum_{i=0}^k\frac{B'_i}{i!}\frac{x^{k-i+1}}{(k-i+1)!})a_k \\=\sum_{k=0}^na_kk!\sum_{i=0}^k\frac{B'_i}{i!}\frac{x^{k-i+1}}{(k-i+1)!} \\=\sum_{k=0}^na_kk!\sum_{i=0}^k\frac{B'_{k-i}}{(k-i)!}\frac{x^{i+1}}{(i+1)!} \\=\sum_{k=0}^na_kk!\sum_{i=1}^{k+1}\frac{B'_{k-i+1}}{(k-i+1)!}\frac{x^i}{i!} \\=\sum_{i=1}^{n+1}\frac{x^i}{i!}\sum_{k=i-1}^na_kk!\frac{B'_{k-i+1}}{(k-i+1)!} \]

那麼設\(f_i=a_ii!\)\(g_i=\frac{B'_i}{i!}\),引入\(g'_i=g_{n-i}\),那麼上面的式子可以接着寫成:

\[\sum_{i=1}^{n+1}\frac{x^i}{i!}\sum_{k=i-1}^nf_kg'_{n-k+i-1} \]

注意到\(g_i=\frac{B'_i}{i!}=\frac{i![x^i]B'(x)}{i!}=[x^i]B'(x)=[x^i](\frac{x}{e^x-1}+x)\),可以\(O(nlogn)\)求,答案的卷積也是\(O(nlogn)\),卷積的第\(n+i-1\)次項除以\(i!\)就是答案的第\(i\)項。
注意到上面式子沒有第\(0\)次項,所以答案第\(0\)次項就是\(a_0\)

#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
#define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
#define add(a,b) (((a)+(b))%mod)
#define inc(a,b) (((a)-(b)+mod)%mod)
#define mul(a,b) (1ll*(a)*(b)%mod)
#define div(a,b) (1ll*(a)*fpow((b,mod-2))%mod)
using namespace std;
typedef cn int cint;
il int rd(){
   rg int x(0); rg char c(gc);
   while(c<'0'||'9'<c)c=gc;
   while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
   return x;
}
cint maxn=2000010,mod=998244353,G=3,invG=332748118;
int n,lim=1,a[maxn],fac[maxn],ifac[maxn],bo[maxn],f[maxn],g[maxn];
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;
}
namespace poly{
	int A[maxn],B[maxn];
	int lim,l,rev,r[maxn];
	il void init(cint &l1,cint &l2){
		lim=1,l=0;
		while(lim<=l1+l2)lim<<=1,++l;
		rev=fpow(lim,mod-2);
		fp(i,0,lim-1)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	}
	il void ntt(int *a,cint &f){
		fp(i,0,lim-1)if(i<r[i])swap(a[i],a[r[i]]);
		for(rg int md=1;md<lim;md<<=1){
			rg int len=md<<1,Gn=fpow(f?G:invG,(mod-1)/len);
			for(rg int l=0;l<lim;l+=len){
				rg int Pow=1;
				for(rg int nw=0;nw<md;++nw,Pow=mul(Pow,Gn)){
					rg int x=a[l+nw],y=mul(Pow,a[l+nw+md]);
					a[l+nw]=add(x,y),a[l+nw+md]=inc(x,y);
				}
			}
		}
	}
	void getinv(int *a,int *f,int n){
		if(n==1){
			f[0]=fpow(a[0],mod-2);
			return;
		}
		getinv(a,f,n>>1);
		init(n-1,n-1);
		fp(i,0,n-1)A[i]=a[i],B[i]=f[i];
		fp(i,n,lim)A[i]=B[i]=0;
		ntt(A,1),ntt(B,1);
		fp(i,0,lim)A[i]=mul(B[i],inc(2,mul(A[i],B[i])));
		ntt(A,0);
		fp(i,0,n-1)f[i]=mul(A[i],rev);
	}
	il void mult(int *a,int *b,int *c,cint &l1,cint &l2,cint &l3){
		init(l1,l2);
		fp(i,0,l1)A[i]=a[i];
		fp(i,0,l2)B[i]=b[i];
		fp(i,l1+1,lim)A[i]=0;
		fp(i,l2+1,lim)B[i]=0;
		ntt(A,1),ntt(B,1);
		fp(i,0,lim)A[i]=mul(A[i],B[i]);
		ntt(A,0);
		fp(i,0,l3)c[i]=mul(A[i],rev);
	}
}
int main(){
	n=rd();
	fp(i,0,n)a[i]=rd();
	fac[0]=1;
	fp(i,1,n+1)fac[i]=mul(fac[i-1],i);
	ifac[n+1]=fpow(fac[n+1],mod-2);
	fb(i,n+1,1)ifac[i-1]=mul(ifac[i],i);
	fp(i,0,n)g[i]=ifac[i+1];
	while(lim<=n)lim<<=1;
	poly::getinv(g,bo,lim),++bo[1];
	fp(i,0,n)f[i]=mul(a[i],fac[i]);
	fp(i,0,n)g[i]=bo[n-i];
	poly::mult(f,g,f,n,n,n<<1);
	printf("%d",a[0]);
	fp(i,1,n+1)printf(" %lld",mul(f[n+i-1],ifac[i]));
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章