跳转至

多项式平移|连续点值平移

多项式平移

多项式平移是简单情况的多项式复合变换,给出 f(x)=∑i=0nfixi 的系数和一个常数 c,求 f(x+c) 的系数,即 f(x)↦f(x+c).

分治法

令

f(x)=f0(x)+x⌊n/2⌋f1(x)

那么

f(x+c)=f0(x+c)+(x+c)⌊n/2⌋f1(x+c)

(x+c)⌊n/2⌋ 的系数为二项式系数,那么

T(n)=2T(n/2)+O(nlog⁡n)=O(nlog2⁡n)

其中 O(nlog⁡n) 为多项式乘法的时间.

Taylor 公式法

对 f(x) 在 c 处应用 Taylor 公式,有

f(x)=f(c)+f′(c)1!(x−c)+f″(c)2!(x−c)2+⋯+f(n)(c)n!(x−c)n

那么

f(x+c)=f(c)+f′(c)1!x+f″(c)2!x2+⋯+f(n)(c)n!xn

观察到对于 t≥0 有

t![xt]f(x+c)=f(t)(c)=∑i=tnfii!ci−t(i−t)!=∑i=0n−tfi+t(i+t)!cii!

令

A0(x)=∑i=0nfn−i(n−i)!xiB0(x)=∑i=0ncii!xi

那么

[xn−t](A0(x)B0(x))=∑i=0n−t([xn−t−i]A0(x))([xi]B0(x))=∑i=0n−tfi+t(i+t)!cii!=t![xt]f(x+c)

二项式定理法

考虑二项式定理 (a+b)n=∑i=0n(ni)aibn−i 那么

f(x+c)=∑i=0nfi(x+c)i=∑i=0nfi(∑j=0i(ij)xjci−j)=∑i=0nfii!(∑j=0ixjj!ci−j(i−j)!)=∑i=0nxii!(∑j=infjj!cj−i(j−i)!)

得到的结果与上述方法相同.

连续点值平移

例题 LOJ 166 拉格朗日插值 2

给出度数小于等于 n 的多项式 f 的连续点值 f(0),f(1),…,f(n),在模 998244353 意义下计算 f(c),f(c+1),…,f(c+n),其中 1≤n≤105,n<m≤108.

Lagrange 插值公式法

考虑 Lagrange 插值公式

f(x)=∑0≤i≤nf(i)∏0≤j≤n∧j≠ix−ji−j=∑0≤i≤nf(i)x!(x−n−1)!(x−i)(−1)n−ii!(n−i)!=x!(x−n−1)!∑0≤i≤nf(i)(x−i)(−1)n−ii!(n−i)!

上式虽然是卷积形式但不能保证分母上 x−i≠0,所以下面仅考虑 c>n 的情况,其他情况(如系数在模素数意义下时须避免 B0(x) 系数的分母出现零)可以分类讨论解决,令

A0(x)=∑0≤i≤nf(i)(−1)n−ii!(n−i)!xiB0(x)=∑i≥01c−n+ixi

那么对于 t≥0 有

[xn+t](A0(x)B0(x))=∑i=0n+t([xi]A0(x))([xn+t−i]B0(x))=∑i=0nf(i)(−1)n−ii!(n−i)!1c+t−i=(c+t−n−1)!(c+t)!f(c+t)

实现中取 B0(x) 需要的部分截断可求出更多点值,且可利用循环卷积.

对问题稍加修改,假设对于某个 d 给出的点值为 f(d),f(d+k),…,f(d+nk),我们可以计算 f(c+d),f(c+d+k),…,f(c+d+nk),视作平移 g(x)=f(d+kx) 的点值 g(0),g(1),…,g(n) 为 g(c/k),g(c/k+1),…,g(c/k+n).

Lagrange 插值公式也给出了通过维护一些前后缀积的线性计算单个点值的方法.

应用

同一行第一类无符号 Stirling 数

例题 P5408 第一类斯特林数·行

在模素数 167772161 意义下求 [n0],[n1],…,[nn],其中 1≤n<262144.

考虑

xn―=∑i=0n[ni]xi,n≥0

其中 xn―=x⋅(x+1)⋯(x+n−1) 为上升阶乘幂,令 fn(x)=xn― 那么

f2n(x)=xn―⋅(x+n)n―=fn(x)fn(x+n)

通过多项式平移可在 O(nlog⁡n) 求出 fn(x+n),问题被缩小为原先的一半即求出 fn(x) 的系数,那么

T(n)=T(n/2)+O(nlog⁡n)=O(nlog⁡n)

模素数意义下阶乘

例题 P5282【模板】快速阶乘算法

求 n!modp,其中 p 为素数且 1≤n<p≤231−1.

令 v=⌊n⌋ 和 g(x)=∏i=1v(x+i) 那么

n!≡(∏i=0v−1g(iv))⋅∏i=v2+1ni(modp)

其中 ∏i=v2+1ni 可在 O(n) 时间计算,我们希望可以快速计算上式的前半部分.

多项式多点求值

g(x) 系数的计算可用上述多项式平移算法在 O(nlog⁡n) 时间得到,但多点求值计算 g(0),g(v),g(2v),…,g(v2−v) 需要 O(nlog2⁡n) 时间.

连续点值平移

令 gd(x)=∏i=1d(x+i),我们可以用 d+1 个点值 gd(0),gd(v),…,gd(dv) 唯一确定这个次数为 d 的多项式,又

g2d(x)=gd(x)gd(x+d)

所以只需 2d+1 个点值可以唯一确定 g2d(x),那么使用连续点值平移计算 gd((d+1)v),gd((d+2)v),…,gd(2dv)(即平移 h(x)=gd(vx) 的点值 h(0),h(1),…,h(d) 为 h(d+1),h(d+2),…,h(2d))和 gd(d),gd(v+d),…,gd(2dv+d)(即平移 h(x)=gd(vx) 的点值 h(0),h(1),…,h(d) 为 h(d/v),h(d/v+1),h(d/v+2),…,h(d/v+2d))后将这两者的对应点值相乘即得 g2d(0),g2d(v),…,g2d(2dv).

由 gd(0),gd(v),…,gd(dv) 计算 gd+1(0),gd+1(v),…,gd+1(dv),gd+1((d+1)v) 考虑

gd+1(x)=(x+d+1)⋅gd(x)

额外增加的一个点值使用线性时间的算法即可.那么在开始时维护 g1(0)=1,g1(v)=v+1 后使用连续点值平移来倍增地维护这些点值,有

T(n)=T(n/2)+O(nlog⁡n)=O(nlog⁡n)

而我们只需要约 n 个点值,所以时间复杂度为 O(nlog⁡n).

模素数意义下二项式系数前缀和

例题 LOJ 6386 组合数前缀和

求 ∑i=0m(ni)mod998244353,其中 0≤m≤n≤9×108.

考虑使用矩阵描述 n!=n⋅(n−1)! 这一步递推,我们有

[n!]=(∏i=0n−1[i+1])[1]

类似的可以将二项式系数前缀和的递推描述为

[(nm+1)∑i=0m(ni)]=[(n−m)/(m+1)011][(nm)∑i=0m−1(ni)]

注意矩阵乘法的顺序,那么

[(nm+1)∑i=0m(ni)]=(∏i=0m[(n−i)/(i+1)011])[10]=1(m+1)!(∏i=0m[n−i0i+1i+1])[10]

令 v=⌊m⌋,考虑维护矩阵

Md(x)=∏i=1d[−x+n+1−i0x+ix+i]=[fd(x)0gd(x)hd(x)]

的点值 Md(0),Md(v),…,Md(dv) 即 fd(0),fd(v),…,fd(dv)、hd(0),…,hd(dv) 和 gd(0),…,gd(dv),又

M2d(x)=∏i=12d[−x+n+1−i0x+ix+i]=(∏i=1d[−x−d+n+1−i0x+d+ix+d+i])(∏i=1d[−x+n+1−i0x+ix+i])=[fd(x+d)0gd(x+d)hd(x+d)][fd(x)0gd(x)hd(x)]=[fd(x+d)fd(x)0gd(x+d)fd(x)+hd(x+d)gd(x)hd(x+d)hd(x)]

且矩阵右下角元素恰为我们在阶乘算法中所维护的,那么

∏i=0m[n−i0i+1i+1]=(∏i=(k+1)vm[n−i0i+1i+1])[fv(kv)0gv(kv)hv(kv)]⋯[fv(0)0gv(0)hv(0)]

可在 O(mlog⁡m) 时间完成计算.

模素数意义下调和数

例题 P5702 调和级数求和

求 ∑i=1ni−1modp,其中 p 为素数且 1≤n<p<230.

记 Hn=∑k=1nk−1,一步递推为

[(n+1)!(n+1)!Hn+1]=[n+101n+1][n!n!Hn]

那么

[[n+11][n+12]]=[n!n!Hn]=(∏i=0n−1[i+101i+1])[10]

在这里 [n+11] 和 [n+12] 为第一类无符号 Stirling 数.维护点值矩阵的方法同上.

整式递推

对于更一般的情况,类似于上述快速阶乘算法的案例,我们期望得到一个怎么样的算法?

例题 P6115【模板】整式递推

现有数列 a 满足 ∀n≥m,∑k=0man−kPk(n)=0,其中 Pk 为不超过 d 次的多项式.
给定所有 Pk 的系数,和 a0,a1,…,am−1,求 an. 对 998244353 取模.n≤6×108,1≤m,d≤7,时限 7s.

为了更系统地描述上述几道例题中构造矩阵的过程,我们引入 λ 矩阵 的概念.

为了实现整式递推,我们应当注意到快速阶乘算法过程中,我们维护的点值其实并不是 n!,而是 ∏i=0T−1(aT+i),即 一对点值之间的倍数关系.

由于整式递推阶数 m 不止是 1 了,我们就 不能直接维护一对数之间的倍数关系了;而是维护出 一对 m 维向量之间的线性变换,即 m×m 的一个矩阵,矩阵的每一项对应于某个多项式的一个点值.

容易发现,对于一般的整式递推远处系数求值问题,我们可以构造

−1P0(n)[P1(n)P2(n)P3(n)⋯Pm−1(n)Pm(n)−P0(n)−P0(n)−P0(n)⋱−P0(n)][an−1an−2an−3⋮an−m+1an−m]=[anan−1an−2⋮an−m+2an−m+1]

设

B(λ)=[P1(λ)P2(λ)P3(λ)⋯Pm−1(λ)Pm(λ)−P0(λ)−P0(λ)−P0(λ)⋱−P0(λ)]

我们先撇开前面的 −1P0(n) 因子不论,我们现在要维护 ∏i=0T−1B(aT+m+i) 这种形式的量,其中乘法自右往左.

容易发现 BT(λ)=∏i=0T−1B(λ+i) 是一个各项次数不高于 dT 的 λ 矩阵,只用 dT+1 个值即可维护.

于是我们维护出 BT(m),BT(m+T),BT(m+2T),…,BT(m+(dT−1)T),BT(m+dT2) 这几个 λ 矩阵的 点值,然后用类似于快速阶乘算法的方式暴力进行多项式点值平移和倍增就好了.

具体地,为了让 t=log2⁡T 抬高 1,我们这么干:

  1. 在 O(m2dTlog⁡(dT)) 时间内获取 BT(p+dT2),BT(p+(dT+1)T),BT(p+(dT+2)T),⋯,BT(p+(2dT−1)T),BT(p+(2dT)dT).
  2. 在 O(m2dTlog⁡(dT)) 时间内获取 BT(p+2dT2),BT(p+(2dT+1)T),BT(p+(2dT+2)T),⋯,BT(p+(3dT−1)T),BT(p+(3dT)dT).
  3. 在 O(m2dTlog⁡(dT)) 时间内获取 BT(p+3dT2),BT(p+(3dT+1)T),BT(p+(3dT+2)T),⋯,BT(p+(4dT−1)T),BT(p+(4dT)dT).
  4. 计算 B2T(v)=BT(v+T)BT(v).

我们每轮花费 O(m2dTlog⁡(dT)) 的复杂度进行平移;同时,我们每轮只用做 Θ(dT) 次矩阵乘法,复杂度可以认为是 O(m3dT).

最后,我们只用做到 T≥n/d 即可.

之前的 −1P0(n) 因子可以用类似的方法解决.

这样,我们预处理的复杂度即为 Θ(nd(m3+m2log⁡(nd))).

考虑查询,我们只用 Θ(n/T) 次向量与矩阵的乘法,以及 O(T) 次暴力转移.

容易发现这部分计算并不是复杂度瓶颈.

因此,该算法的总复杂度为 Θ(nd(m3+m2log⁡(nd))).

编码时,我们可以使用循环卷积的技巧来减小 NTT 的常数.

在实际应用时,我们往往是对一个已知的微分有限的 GF 提取其远处系数,从而 m,d 均为常数,也即做到了 Θ(nlog⁡n) 的远处系数求值.

参考文献

  • Alin Bostan, Pierrick Gaudry, and Eric Schost. Linear recurrences with polynomial coefficients and application to integer factorization and Cartier–Manin operator.
  • Min_25 的博客
  • ZZQ 的博客 - 阶乘模大质数