求解自然数幂和的n种方法

问题的引入

给定 n , k n,k n,k ∑ i = 1 n i k \sum_{i=1}^ni^k i=1nik

1. 循环

四年级应该会循环了。

能做到 O ( n k ) O(nk) O(nk)的优秀时间复杂度。

2. 快速幂

五年级学了快速幂之后就能做到 O ( n l o g 2 k ) O(nlog_2k) O(nlog2k)

请不要小看这个算法。有时候在特定的情况下(例如 n n n很小,或 1 → n 1\rightarrow n 1n的距离变得很小时),这个复杂度真的很优秀。

3. 差分法

六年级应该知道差分和二项式定理了。那么: ( a + 1 ) k − a k = ∑ i = 0 k − 1 C k i a i (a+1)^k-a^k=\sum_{i=0}^{k-1}C_k^ia^i (a+1)kak=i=0k1Ckiai

于是:
( n + 1 ) k − 1 = ∑ i = 1 n ( i + 1 ) k − i k                  = ∑ i = 1 n ∑ j = 0 k − 1 C k j i j                    = ∑ i = 0 k − 1 C k i ∑ j = 1 n j i                = ∑ i = 0 k − 1 C k i S ( i ) (n+1)^k-1 =\sum_{i=1}^n (i+1)^k-i^k \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{i=1}^n\sum_{j=0}^{k-1}C_k^ji^j\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{i=0}^{k-1}C_k^i\sum_{j=1}^n j^i\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{i=0}^{k-1}C_k^i S(i) (n+1)k1=i=1n(i+1)kik                =i=1nj=0k1Ckjij                  =i=0k1Ckij=1nji              =i=0k1CkiS(i) ∴ ( n + 1 ) k + 1 − 1 = ∑ i = 0 k C k + 1 i S ( i ) \therefore (n+1)^{k+1}-1=\sum_{i=0}^kC_{k+1}^iS(i) (n+1)k+11=i=0kCk+1iS(i)

i = k i=k i=k时移项,可以得到 ( k + 1 ) S ( k ) = ( n + 1 ) k + 1 − 1 − ∑ i = 0 k − 1 C k + 1 i S ( i ) (k+1)S(k)=(n+1)^{k+1}-1-\sum_{i=0}^{k-1}C_{k+1}^iS(i) (k+1)S(k)=(n+1)k+11i=0k1Ck+1iS(i)

所以 S ( k ) = ( n + 1 ) k + 1 − 1 − ∑ i = 0 k − 1 C k + 1 i S ( i ) k + 1 S(k)=\frac{(n+1)^{k+1}-1-\sum_{i=0}^{k-1}C_{k+1}^iS(i)}{k+1} S(k)=k+1(n+1)k+11i=0k1Ck+1iS(i)

同时仔细观察这个式子,我们发现, k k k次方和的求和公式是 k + 1 k+1 k+1次的。归纳证明即可。

3. 倍增

初一应该会倍增了,所以我们令 f n , k = ∑ i = 1 n i k f_{n,k}=\sum_{i=1}^ni^k fn,k=i=1nik

n n n是奇数的时候直接由 f n − 1 , k + n k f_{n-1,k}+n^k fn1,k+nk转移过来。偶数的时候拆开来,运用简单的二项式定理,一波式子推得: f ( n , k ) = f ( n 2 , k ) + ∑ j = 0 k C k j ∗ f ( n 2 , j ) ∗ n 2 k − j f(n,k)=f(\frac{n}{2},k)+\sum_{j=0}^kC_k^j*f(\frac{n}{2},j)*\frac{n}{2}^{k-j} f(n,k)=f(2n,k)+j=0kCkjf(2n,j)2nkj

每一层的 f n , k f_{n,k} fn,k我们计算的时间复杂度都是 O ( k 2 ) O(k^2) O(k2)的, l o g n log_n logn层,时间复杂度 O ( k 2 l o g n ) O(k^2log_n) O(k2logn).

4. 高斯消元

初一应该会高斯消元了。这是个大脑洞。虽然时间复杂度比上一个还劣一些。

根据 k k k次方和的求和公式是 k + 1 k+1 k+1次的,所以列出 k + 2 k+2 k+2条式子就可以唯一确定这个多项式。

时间复杂度 O ( k 3 ) O(k^3) O(k3).

5. 第一类斯特林数

初二来学习一下斯特林数。

第一类斯特林数我们一般清楚的是它的组合意义,即把 n n n个元素分成 k k k个圆排列的方案。根据组合意义,我们不难推出它的式子是 S u ( n , m ) = S u ( n − 1 , m − 1 ) + ( n − 1 ) S u ( n − 1 , m ) S_u(n,m)=S_u(n-1,m-1)+(n-1)S_u(n-1,m) Su(n,m)=Su(n1,m1)+(n1)Su(n1,m)

但事实上,我们求解自然数幂和需要用到的是它的原始定义
x n ↓ = x ⋅ ( x − 1 ) ⋅ ( x − 2 ) ⋯ ( x − n + 1 ) = ∑ k = 0 n s s ( n , k ) ⋅ x k x^{n\downarrow}=x\cdot (x-1)\cdot (x-2)\cdots (x-n+1)=\sum_{k=0}^ns_s(n,k)\cdot x^k xn=x(x1)(x2)(xn+1)=k=0nss(n,k)xk x n ↑ = x ⋅ ( x + 1 ) ⋅ ( x + 2 ) ⋯ ( x + n − 1 ) = ∑ k = 0 n s u ( n , k ) ⋅ x k x^{n\uparrow}=x\cdot (x+1)\cdot (x+2)\cdots(x+n-1)=\sum_{k=0}^ns_u(n,k)\cdot x^k xn=x(x+1)(x+2)(x+n1)=k=0nsu(n,k)xk

这里需要注意,第一类斯特林数根据定义分成了有符号 S s S_s Ss和无符号 S u S_u Su两种。事实上,我们可以很轻松的从这个原始定义推出它的组合意义

因为 ∑ k = 0 n S u ( n , k ) ⋅ x k = x n ↑ = x ( n − 1 ) ↑ ⋅ ( x + n − 1 ) \sum_{k=0}^nS_u(n,k)·x^k=x^{n\uparrow}=x^{(n-1)\uparrow}·(x+n-1) k=0nSu(n,k)xk=xn=x(n1)(x+n1) = ∑ k = 0 n − 1 S u ( n − 1 , k ) x k + 1 + ( n − 1 ) ∑ k = 0 n − 1 S u ( n − 1 , k ) x k =\sum_{k=0}^{n-1}S_u(n-1,k)x^{k+1}+(n-1)\sum_{k=0}^{n-1}S_u(n-1,k)x^k =k=0n1Su(n1,k)xk+1+(n1)k=0n1Su(n1,k)xk

对比两边 x m x^m xm的系数,可以得到 S u ( n , m ) = S u ( n − 1 , m − 1 ) + ( n − 1 ) S u ( n − 1 , m ) S_u(n,m)=S_u(n-1,m-1)+(n-1)S_u(n-1,m) Su(n,m)=Su(n1,m1)+(n1)Su(n1,m)继续推有符号的,可以得到 S s ( n , m ) = S s ( n − 1 , m − 1 ) − ( n − 1 ) S s ( n − 1 , m ) S_s(n,m)=S_s(n-1,m-1)-(n-1)S_s(n-1,m) Ss(n,m)=Ss(n1,m1)(n1)Ss(n1,m)事实上,我们可以完全不用记第一类斯特林数的组合意义,通过公式直接推出来即可。当然记了更好,还可以验证。

所以,根据第一类斯特林数的的定义,得到: ∏ x = 0 k − 1 ( n − x ) = ∑ k = 0 n S s ( n , k ) x k \prod_{x=0}^{k-1}(n-x)=\sum_{k=0}^nS_s(n,k)x^k x=0k1(nx)=k=0nSs(n,k)xk

于是我们可以得到一个显然的式子是: n m = n m ↓ − ∑ k = 0 m − 1 S s ( m , k ) ⋅ n k n^m=n^{m\downarrow}-\sum_{k=0}^{m-1}S_s(m,k)·n^k nm=nmk=0m1Ss(m,k)nk

我们继续推,发现下降幂的和是可以写成一个组合数的形式的,比方说 ∑ i = m n i m ↓ = ∑ i = m n i ! m ! ( i − m ) ! m ! = m ! ∑ i = m n ( i m ) = m ! ( n + 1 m + 1 ) \sum_{i=m}^ni^{m\downarrow}=\sum_{i=m}^n\frac{i!m!}{(i-m)!m!}=m!\sum_{i=m}^n\binom{i}{m}=m!\binom{n+1}{m+1} i=mnim=i=mn(im)!m!i!m!=m!i=mn(mi)=m!(m+1n+1)

而后面那一坨式子也是可以化简的,比方说 ∑ i = 0 n ∑ k = 0 m − 1 S s ( m , k ) ⋅ i k = ∑ k = 0 m − 1 S s ( m , k ) ∑ i = 0 n i k \sum_{i=0}^n\sum_{k=0}^{m-1}S_s(m,k)·i^k=\sum_{k=0}^{m-1}S_s(m,k)\sum_{i=0}^ni^k i=0nk=0m1Ss(m,k)ik=k=0m1Ss(m,k)i=0nik

发现后面那条式子 ∑ i = 0 n i k \sum_{i=0}^ni^k i=0nik k k k是降了阶的,所以可以边处理边记录一下,就不用重新算了,时间复杂度就变成了 O ( k 2 ) O(k^2) O(k2)。而处理 S s ( m , k ) S_s(m,k) Ss(m,k)也是 O ( k 2 ) O(k^2) O(k2)级别。

事实上如果当你升入初三, S s ( m , k ) S_s(m,k) Ss(m,k)就可以运用分治 N T T NTT NTT做到 O ( k l o g 2 k ) O(klog^2k) O(klog2k)了,虽然然并卵。

但请注意,这种方法虽然时间复杂度是 O ( k 2 ) O(k^2) O(k2)级别的,但是它并非没有什么用,因为它——不用做除法

6. 第二类斯特林数

第二类斯特林数的组合意义就是 n n n个元素分成 m m m个集合,且集合非空的方案数。

基本性质是 { n m } = { n − 1 m − 1 } + m ⋅ { n − 1 m } \begin{Bmatrix}n\\m\end{Bmatrix}=\begin{Bmatrix}n-1\\m-1\end{Bmatrix}+m\cdot \begin{Bmatrix}n-1\\m\end{Bmatrix} {nm}={n1m1}+m{n1m}

考虑它的通项公式,可以先把所有集合标号,最后除以集合的阶乘即可,那么考虑容斥,枚举非空集合个数 i i i,可以得到 { n m } = 1 m ! ∑ i = 0 m ( − 1 ) i ( m i ) ( m − i ) n \begin{Bmatrix}n\\m\end{Bmatrix}=\frac 1 {m!}\sum_{i=0}^m(-1)^i\binom mi(m-i)^n {nm}=m!1i=0m(1)i(im)(mi)n

接下来继续推导自然数幂和。

显然!! i k = ∑ j = 0 k { k j } i j ↓ i^k=\sum_{j=0}^k\left\{\begin{array}{c}{k}\\{j}\end{array}\right\}i^{j\downarrow} ik=j=0k{kj}ij

继续推导 ∑ i = 1 n i k = ∑ i = 1 n ∑ j = 0 k { k j } i j ↓ \sum_{i=1}^ni^k=\sum_{i=1}^n\sum_{j=0}^k\left\{\begin{array}{c}{k}\\{j}\end{array}\right\}i^{j\downarrow} i=1nik=i=1nj=0k{kj}ij = ∑ i = 1 n ∑ j = 0 k { k j } j ! ( i j ) =\sum_{i=1}^n\sum_{j=0}^k\left\{\begin{array}{c}{k}\\{j}\end{array}\right\} j!\left(\begin{array}{c}{i}\\{j}\end{array}\right) =i=1nj=0k{kj}j!(ij) = ∑ j = 0 k { k j } j ! ∑ i = j n ( i j ) =\sum_{j=0}^k\left\{\begin{array}{c}{k}\\{j}\end{array}\right\} j!\sum_{i=j}^n\left(\begin{array}{c}{i}\\{j}\end{array}\right) =j=0k{kj}j!i=jn(ij) = ∑ j = 0 k { k j } j ! ( n + 1 j + 1 ) =\sum_{j=0}^k\left\{\begin{array}{c}{k}\\{j}\end{array}\right\} j!\binom{n+1}{j+1} =j=0k{kj}j!(j+1n+1)

很明显,除去预处理第二类斯特林数的复杂度,后面是一样不用做除法的,可以做到 O ( k ) O(k) O(k).

那么时间复杂度决定于预处理第二类斯特林数的复杂度。显然可以用 O ( k 2 ) O(k^2) O(k2)递推。

然而事实上,我们来看看斯特林数的通项公式: { n m } = 1 m ! ∑ i = 0 m ( − 1 ) i ( m i ) ( m − i ) n \begin{Bmatrix}n\\m\end{Bmatrix}=\frac 1 {m!}\sum_{i=0}^m(-1)^i\binom mi(m-i)^n {nm}=m!1i=0m(1)i(im)(mi)n

一拼凑,咦~ { n m } = ∑ i = 0 m ( − 1 ) i i ! ⋅ ( m − i ) n ( m − i ) ! \begin{Bmatrix}n\\m\end{Bmatrix}=\sum_{i=0}^m\frac{(-1)^i}{i!}\cdot\frac{(m-i)^n}{(m-i)!} {nm}=i=0mi!(1)i(mi)!(mi)n

这原来可以写成形如 ∑ i = 0 m f ( i ) ∗ g ( m − i ) \sum_{i=0}^mf(i)*g(m-i) i=0mf(i)g(mi)的卷积形式。于是第一个多项式的第 i i i项系数是 ( − 1 ) i i ! \frac {(-1)^i}{i!} i!(1)i,另一个多项式的第 i i i项系数是 i n i ! \frac {i^n}{i!} i!in,卷积后第 i i i项的系数就是 { n i } \begin{Bmatrix}n\\i\end{Bmatrix} {ni}.

于是愉快的将时间变成了 O ( K l o g K ) O(KlogK) O(KlogK)

7. 差分表

初三来学习一下差分表吧。

对于任何一个序列 a 0 , a 1 , . . . , a n , . . . a_0, a_1, ... , a_n, ... a0,a1,...,an,...我们都可以定义它的差分序列 Δ a 0 , Δ a 1 , . . . , Δ a n , . . . \Delta a_0, \Delta a_1, ... ,\Delta a_n, ... Δa0,Δa1,...,Δan,...,其中 Δ a i = a i + 1 − a i \Delta a_i=a_{ i+1 }-a_i Δai=ai+1ai

类似的,我们可以构造序列 { Δ a n } \{\Delta a_n\} {Δan}的二阶、三阶… k k k阶差分序列。 不妨记为 { Δ 2 a n } , . . . , { Δ k a n } \{\Delta^2 a_n\},...,\{\Delta^k a_n\} {Δ2an},...,{Δkan}

令序列是一个 p p p次多项式,那么差分表一个很重要且很显然的性质是: ∀ n > = 0 , Δ p + 1 a n = 0 \forall n>=0, \Delta^{p+1}a_n = 0 n>=0,Δp+1an=0这是由于每次差分都必然会把最高次项消去!

另外一个很重要的性质就是差分表的线性性,即如果 f n = k 1 g n + k 2 h n f_n=k_1g_n+k_2h_n fn=k1gn+k2hn,那么一定有 ∀ p , n , Δ p f n = k 1 Δ p g n + k 2 Δ p h n \forall p, n, \Delta^p f_n=k_1\Delta^p g_n + k_2\Delta^p h_n p,n,Δpfn=k1Δpgn+k2Δphn

而其最重要的一个性质就是,任何一个 p p p阶多项式,都必定可以由其差分表的第一条对角线确定。为了证明这个结论,不妨先考虑最简单的情况:

差分表的一条对角线为 0 , . . . , 0 , 1 , 0 , . . . 0, ..., 0, 1, 0, ... 0,...,0,1,0,...,即第一条对角线上只有第 p p p个位置为 1 1 1,其他都为 0 0 0,那么可以写出这个序列的通项公式 f n = c ( n ) ( n − 1 ) ( n − 2 ) . . . ( n − p + 1 ) f_n=c(n)(n-1)(n-2)...(n-p+1) fn=c(n)(n1)(n2)...(np+1)

代入 n = p , f p = 1 n=p,f_p=1 n=p,fp=1,得到 c = 1 p ! c=\frac{1}{p!} c=p!1所以可以得到 f n = n ! p ! ( n − p ) ! = ( n p ) f_n=\frac{n!}{p!(n-p)!}=\tbinom{n}{p} fn=p!(np)!n!=(pn)

那么根据差分表的线性性,我们就可以得知 f n = ∑ i = 0 p c i ( n i ) f_n=\sum_{i=0}^p c_i\tbinom{n}{i} fn=i=0pci(in)
由于 ∑ k = 0 n f ( k ) = ∑ k = 0 p c k ( n + 1 k ) \sum_{k = 0}^n f(k) = \sum_{k = 0}^p c_k {n + 1 \choose k} k=0nf(k)=k=0pck(kn+1)所以利用差分表,我们可以在 O ( p 2 ) O(p^2) O(p2)的时间复杂度求解类似于 ∑ i = 0 n f i \sum_{i=0}^n f_i i=0nfi的式子。

回到自然数幂和的问题上,我们把 f i = i k f_i=i^k fi=ik代入计算前 p p p项的值,通过 p 2 p^2 p2的时间复杂度处理出差分表的第一条对角线,设这个对角线为 c ( p , 0 ) , c ( p , 1 ) , c ( p , 2 ) , … , c ( p , p ) c(p, 0), c(p, 1), c(p, 2), \dots, c(p, p) c(p,0),c(p,1),c(p,2),,c(p,p),那么答案就是 ∑ k = 0 p c ( p , k ) ( n + 1 k ) \sum_{k = 0}^p c(p, k){n + 1 \choose k} k=0pc(p,k)(kn+1)

8. 伯努利数

初三再来学一学伯努利数吧。

根据伯努利数的生成函数定义,可知 x e x − 1 = ∑ i ≥ 0 B i ∗ x i i ! \frac{x}{e^{x}-1} = \sum_{i\geq 0} B_{i}*\frac{x^{i}}{i!} ex1x=i0Bii!xi

由于 x e x − 1 ⋅ ( e x − 1 ) = x \frac{x}{e^x-1}·(e^x-1)=x ex1x(ex1)=x

[ x n ] x e x − 1 ⋅ ( e x − 1 ) = ∑ i = 0 n − 1 B i i ! ⋅ 1 ( n − i ) ! = [ n = 1 ] [x^n]\frac{x}{e^x-1}·(e^x-1)=\sum_{i=0}^{n-1}\frac{B_i}{i!}·\frac{1}{(n-i)!}=[n=1] [xn]ex1x(ex1)=i=0n1i!Bi(ni)!1=[n=1]

两边都乘上 n ! n! n!可以得到 ∑ i = 0 n − 1 ( n i ) B i = [ n = 1 ] \sum_{i=0}^{n-1}\binom{n}{i}B_i=[n=1] i=0n1(in)Bi=[n=1]

这是伯努利数的一个基本性质。后面会用到。

我们再定义一个多项式 B n ( t ) B_n(t) Bn(t)表示 B n ( t ) = ∑ k = 0 n − 1 B k ∗ t n − k ∗ C n k B_{n}(t) = \sum_{k=0}^{n-1} B_{k} * t^{n-k}*C_{n}^{k} Bn(t)=k=0n1BktnkCnk

然后我们发现
B n ( t + 1 ) − B n ( t ) = ∑ k = 0 n − 1 B k ∗ ⟮ ( t + 1 ) n − k − t n − k ⟯ C n k B_n(t+1)-B_n(t)=\sum_{k=0}^{n-1} B_{k}*\lgroup(t+1)^{n-k} - t^{n-k}\rgroup C_{n}^{k} Bn(t+1)Bn(t)=k=0n1Bk(t+1)nktnkCnk = ∑ k = 0 n − 1 B k ∗ ( ∑ i = 0 n − k − 1 C n − k i ∗ t i ) ∗ C n k =\sum_{k=0}^{n-1} B_{k}*(\sum_{i=0}^{n-k-1} C_{n-k}^{i} * t^{i})* C_{n}^{k} =k=0n1Bk(i=0nk1Cnkiti)Cnk = ∑ k = 0 n − 1 B k ∗ ( ∑ i = 0 n − k − 1 C n − k i ∗ t i ∗ C n k ) =\sum_{k=0}^{n-1} B_{k}*(\sum_{i=0}^{n-k-1} C_{n-k}^{i} * t^{i}*C_{n}^{k}) =k=0n1Bk(i=0nk1CnkitiCnk) = ∑ i = 0 n − 1 B k ∗ ∑ i = 0 n − k − 1 n ! t i i ! k ! ( n − k − i ) ! =\sum_{i=0}^{n-1}B_k*\sum_{i=0}^{n-k-1}\frac{n!t^i}{i!k!(n-k-i)!} =i=0n1Bki=0nk1i!k!(nki)!n!ti = ∑ k = 0 n − 1 B k ∗ ( ∑ i = 0 n − k − 1 C n − i k ∗ t i ∗ C n i ) =\sum_{k=0}^{n-1} B_{k}*(\sum_{i=0}^{n-k-1} C_{n-i}^{k} * t^{i}*C_{n}^{i}) =k=0n1Bk(i=0nk1CniktiCni) = ∑ i = 0 n − 1 C n i ∗ t i ∗ ∑ k = 0 n − 1 − i B k ∗ C n − i k =\sum_{i=0}^{n-1} C_{n}^{i}*t^{i}*\sum_{k=0}^{n-1-i} B_{k}*C_{n-i}^{k} =i=0n1Cnitik=0n1iBkCnik

注意到后面只有当 n − i − 1 = 0 n-i-1=0 ni1=0时值为 1 1 1,于是 B n ( t + 1 ) − B n ( t ) = n ∗ t n − 1 B_n(t+1)-B_n(t)=n*t^{n-1} Bn(t+1)Bn(t)=ntn1

然后我们考虑差分,就有 ∑ t = 0 n − 1 B k ( t + 1 ) − B k ( t ) = k ⋅ ∑ i = 0 n − 1 i k − 1 \sum_{t=0}^{n-1}B_k(t+1)-B_k(t)=k·\sum_{i=0}^{n-1}i^{k-1} t=0n1Bk(t+1)Bk(t)=ki=0n1ik1 B k + 1 ( n + 1 ) = ( k + 1 ) ⋅ ∑ i = 0 n i k B_{k+1}(n+1)=(k+1)·\sum_{i=0}^ni^k Bk+1(n+1)=(k+1)i=0nik

可得自然数幂和 ∑ i = 0 n i k = 1 k + 1 ⋅ ∑ i = 0 k B i n k + 1 − i ( k + 1 i ) \sum_{i=0}^ni^k=\frac{1}{k+1}·\sum_{i=0}^kB_in^{k+1-i}\binom{k+1}{i} i=0nik=k+11i=0kBink+1i(ik+1)

问题转化成了求 B i B_i Bi,注意到它的生成函数定义,事实上我们只需要求 ∑ i ≥ 0 x i ( i + 1 ) ! \sum_{i\ge 0}\frac{x^i}{(i+1)!} i0(i+1)!xi在模 x k + 1 x^{k+1} xk+1的逆元即可。

时间复杂度 O ( K L o g K ) O(KLogK) O(KLogK),当然如果递推的话,也是可以轻松做到 O ( k 2 ) O(k^2) O(k2)的。

9. 拉格朗日插值法

两大作用:

  1. 快速根据点值逼近原函数.

  2. 取点值对大于 n n n唯一确定 n n n次多项式.

Example

例如对于 ∑ i = 1 n i = n ( n + 1 ) 2 \sum\limits_{i=1}^ni=\frac{n(n+1)}{2} i=1ni=2n(n+1)

我们知道它的通项公式是二次的,所以我们只需要三个点值对就可以唯一确定这个多项式: ( 1 , 1 ) , ( 2 , 3 ) , ( 3 , 6 ) (1,1),(2,3),(3,6) (1,1),(2,3),(3,6)

General method

对于已知的 n + 1 n+1 n+1个点对 ( x 0 , y 0 ) , ( x 1 , y 1 ) . . . ( x n , y n ) (x_0,y_0),(x_1,y_1)...(x_n,y_n) (x0,y0),(x1,y1)...(xn,yn),求 n + 1 n+1 n+1个函数 f i f_i fi,使得该函数在 x i x_i xi处取得对应的 y i y_i yi值,其余 x j x_j xj处为 0 0 0,最后把这 n + 1 n+1 n+1个函数线性结合即可。

f i ( x ) = ∏ j ≠ i ( x − x j ) ∏ j ≠ i ( x i − x j ) ∗ y i f_i(x)=\frac{\prod\limits_{j\neq i}(x-x_j)}{\prod\limits_{j\neq i}(x_i-x_j)}*y_i fi(x)=j̸=i(xixj)j̸=i(xxj)yi g ( x ) = ∑ i = 0 n f i ( x ) g(x)=\sum_{i=0}^nf_i(x) g(x)=i=0nfi(x)

Practice

例如我们要求自然数幂和.

各种方法可以证明 i k i^k ik的和是 k + 1 k+1 k+1次的, 所以我们只需要给出 k + 2 k+2 k+2个点值表达,就可以求得通项公式.

S ( n ) = ∑ i = 1 n i k S(n)=\sum_{i=1}^n i^k S(n)=i=1nik, 则
S ( n ) = ∑ i = 1 k + 2 y i ∏ j = 1 , i ≠ j k + 2 n − x j x i − x j = ∑ i = 1 k + 2 y i ∏ j = 1 , i ≠ j k + 2 ( n − j ) ∏ j = 1 , i ≠ j k + 2 ( i − j ) S(n)=\sum_{i=1}^{k+2}y_i\prod_{j=1,i\neq j}^{k+2}\frac {n-x_j}{x_i-x_j}=\sum_{i=1}^{k+2}y_i\frac {\prod_{j=1,i\neq j}^{k+2}(n-j)}{\prod_{j=1,i\neq j}^{k+2}(i-j)} S(n)=i=1k+2yij=1,i̸=jk+2xixjnxj=i=1k+2yij=1,i̸=jk+2(ij)j=1,i̸=jk+2(nj)

那么时间复杂度就在预处理 y i y_i yi上面了, 利用线筛,可以做到 O ( k ) O(k) O(k)级别.

后面的那一部分可以预处理,具体的说,就有: S ( n ) = ∑ i = 1 k + 2 y i p r e [ i − 1 ] s u f [ i + 1 ] [ ( − 1 ) k + 2 − i ( i − 1 ) ! ( k + 2 − i ) ! ] − 1 S(n)=\sum_{i=1}^{k+2}y_ipre[i-1]suf[i+1][(-1)^{k+2-i}(i-1)!(k+2-i)!]^{-1} S(n)=i=1k+2yipre[i1]suf[i+1][(1)k+2i(i1)!(k+2i)!]1

C o d e Code Code

时间复杂度: O ( k l n k l o g 2 k ) = O ( k ) O(\frac{k}{ln_k}log_2k)=O(k) O(lnkklog2k)=O(k).

另外,注意逆元要预处理,实测: k = 1 e 7 k=1e7 k=1e7 0.9 s 0.9s 0.9s,可以说是非常优秀了

#include <bits/stdc++.h> 

#define F(i,a,b) for (int i = a; i <= b; i ++)
#define G(i,a,b) for (int i = a; i >= b; i --)

const int Mo = 998244353, M = 1e6 + 10;

using namespace std;

int l, r, k, m, y[M], z[M], jc[M], suf[M], pre[M];
bool bz[M];

int ksm(int x, int y) {
	int ans = 1;
	for (; y; y >>= 1, x = (1ll * x * x) % Mo)
		if (y & 1)
			ans = (1ll * ans * x) % Mo;
	return ans;
}

void Init() {
	scanf("%d%d%d", &l,&r,&k), y[1] = 1, m = k + 2;
	F(i, 2, m) {
		if (!bz[i])
			z[++ z[0]] = i, y[i] = ksm(i, k);
		F(j, 1, z[0]) {
			if (z[j] * i > m) break;
			bz[z[j] * i] = 1;
			y[z[j] * i] = (1ll * y[z[j]] * y[i]) % Mo;
			if (i % z[j] == 0) break;
		}
	}
	F(i, 2, m)
		y[i] = (y[i - 1] + y[i]) % Mo;
	jc[0] = 1;
	F(i, 1, m)
		jc[i] = 1ll * jc[i - 1] * i % Mo;
	jc[m] = ksm(jc[m], Mo - 2);
	G(i, m - 1, 1)
		jc[i] = 1ll * jc[i + 1] * (i + 1) % Mo;
}

int Solve(int n) {
	pre[0] = suf[m + 1] = 1;
	F(i, 1, m)
		pre[i] = 1ll * pre[i - 1] * (n - i) % Mo;
	G(i, m, 1)
		suf[i] = 1ll * suf[i + 1] * (n - i) % Mo;

	int Ans = 0;
	F(i, 1, m)
		Ans = (Ans + 1ll * y[i] * pre[i - 1] % Mo * suf[i + 1] % Mo * (((k-i+2)&1) ? (-1) : 1) * jc[i - 1] % Mo * jc[k + 2 - i] % Mo) % Mo;
	return Ans;
}

int main() {
	Init();

	printf("%d\n", (Solve(r) - Solve(l - 1) + Mo) % Mo);
}
  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值