这个坑开这很久了,一直没机会填。趁着省选前复习的机会赶工。。。。
多项式取模的定义
说到取模,我们大概会想到这个式子:
A
(
x
)
÷
B
(
x
)
=
D
(
x
)
.
.
.
.
.
R
(
x
)
A(x)\div B(x)=D(x).....R(x)
A(x)÷B(x)=D(x).....R(x)
其中A是被除多项式,B是除多项式,D是A除以B的"商",而R就称作余多项式。
换个更直接的说法:即有
A
(
x
)
=
B
(
x
)
D
(
x
)
+
R
(
x
)
A(x)=B(x)D(x)+R(x)
A(x)=B(x)D(x)+R(x)
想想整数除法中,余数必须要小于除数。类似的,我们要保证R(x)的次数严格小于B(x)。
也就是,假如
A
(
x
)
A(x)
A(x)的最高次项是
x
n
x^n
xn,
B
(
x
)
B(x)
B(x)的最高次项是
x
m
x^m
xm,
(这里的最高次项指系数不为0的次数最大项。)
那么
D
(
x
)
D(x)
D(x)的最高次项必须为
x
n
−
m
x^{n-m}
xn−m,
R
(
x
)
R(x)
R(x)的最高次项必须小于
x
m
x^m
xm
考虑一个暴力取模的做法,每次看 A ( x ) A(x) A(x)最高次项是否大于等于 x m x^m xm,如果是,则通过加减 B ( x ) B(x) B(x)或其倍数消去这一项。直到不能再进行这个操作,我们便得到了余多项式。由此,对于A除以B来说,余多项式是唯一的,商也是唯一的。
快速做多项式取模
A
(
x
)
=
B
(
x
)
D
(
x
)
+
R
(
x
)
A(x)=B(x)D(x)+R(x)
A(x)=B(x)D(x)+R(x)
观察上式,已知A和B的时候,我们想要求两个未知(但又被A,B唯一确定了的)多项式:D和R。
假如没有R(x),要你求 A ( x ) = B ( x ) D ( x ) A(x)=B(x)D(x) A(x)=B(x)D(x)中的D(x),你会做吗?
答案是多项式求逆的简单应用。对B(x)求逆即可,再将A乘上B的逆,就是D了。
题外话:
当然,假如B不止有常数项的话,B的逆是无穷项的。而我们知道D的最高次项,只需要做这么多的逆就够了。
那么,能不能通过一些手段,消去其中R(x)的影响呢?
当然可以,(不然我写这东西干嘛…)
我们将A(x)的所有系数翻转,使得原本是
x
n
x^n
xn的系数变成常数项,原本是
x
n
−
1
x^{n-1}
xn−1的系数变成一次项系数…
这个操作如何用式子表示呢:
x
n
A
(
1
x
)
x^nA(\frac 1 x)
xnA(x1)
考虑对置顶的式子左右同时做该变换,等式仍然成立:
x
n
A
(
1
x
)
=
x
n
(
B
(
1
x
)
D
(
1
x
)
+
R
(
1
x
)
)
x^nA(\frac 1 x) = x^n(B(\frac 1 x)D(\frac1 x)+R(\frac 1x ))
xnA(x1)=xn(B(x1)D(x1)+R(x1))
对于左边,我们已经知道这是在将系数翻转。那对于右边呢?不妨将
x
n
x^n
xn分进去。
x
n
A
(
1
x
)
=
x
m
B
(
1
x
)
x
n
−
m
D
(
1
x
)
+
x
m
−
1
R
(
1
x
)
x
n
−
m
+
1
x^nA(\frac 1 x) = x^mB(\frac 1 x)x^{n-m}D(\frac1 x)+x^{m-1}R(\frac 1x )x^{n-m+1}
xnA(x1)=xmB(x1)xn−mD(x1)+xm−1R(x1)xn−m+1
怎么样,你看出来了吗?
事实上,我们是对A,B,D,R同时做系数翻转(当然,需要假设其最高次项分别是
x
n
,
x
m
,
x
n
−
m
,
x
m
−
1
x^n,x^m,x^{n-m},x^{m-1}
xn,xm,xn−m,xm−1)。再将R向高次移动
n
−
m
+
1
n-m+1
n−m+1位。
改写一下,
A
′
(
x
)
=
B
′
(
x
)
D
′
(
x
)
+
R
′
(
x
)
x
n
−
m
+
1
A'(x)=B'(x)D'(x)+R'(x)x^{n-m+1}
A′(x)=B′(x)D′(x)+R′(x)xn−m+1
好看多了。这时候,让你求D’,你会求吗?
答案很简单,由于 D 和 D ′ D和D' D和D′的最高次项是 x n − m x^{n-m} xn−m,可以直接求 D ′ D' D′在模 x n − m + 1 x^{n-m+1} xn−m+1意义下的值。然后还原回D,这便是完整的D。
那么我们的最关键问题迎刃而解,剩下的部分就是手到擒来。
关键思想是先系数翻转,再把余多项式用模意义抹去。
常系数齐次线性递推
有了多项式取模,这事实上是个很简单的东西。
这个线性递推中的任意项
g
x
g_x
gx,都可以用
g
1
,
g
2
.
.
.
g
k
g_1,g_2...g_k
g1,g2...gk表示出来。
不妨设
g
x
=
∑
i
=
1
k
a
i
g
i
g_x=\sum_{i=1}^{k}a_ig_i
gx=i=1∑kaigi
有一个位移操作是这样的,假如上式,那么则有
g
x
+
d
=
∑
i
=
1
k
a
i
g
i
+
d
g_{x+d}=\sum_{i=1}^{k}a_ig_{i+d}
gx+d=i=1∑kaigi+d
道理很简单,不证明了。
那么考虑在已知 g n 和 g m g_n和g_m gn和gm的情况下,表示 g n + m g_{n+m} gn+m。
g
n
+
m
=
∑
i
=
1
k
a
i
g
m
+
i
g_{n+m}=\sum_{i=1}^{k}a_ig_{m+i}
gn+m=i=1∑kaigm+i
这里的a1,a2…ak是gn的表示系数。
进一步,表示gm。
g
n
+
m
=
∑
i
=
1
k
a
i
∑
j
=
1
k
b
j
g
j
+
i
g_{n+m}=\sum_{i=1}^{k}a_i\sum_{j=1}^kb_jg_{j+i}
gn+m=i=1∑kaij=1∑kbjgj+i
这里的b1,b2…bk是gm的表示系数。
g n + m = ∑ i = 1 2 k g i ∑ u = 1 k a u b i − u g_{n+m}=\sum_{i=1}^{2k}g_i\sum_{u=1}^ka_ub_{i-u} gn+m=i=1∑2kgiu=1∑kaubi−u
后面的系数是标准的卷积形式,使用FFT加速即可计算。
假若能快速把1…2k的系数化成1…k的系数,就可以实现倍增求g。
暴力怎么做?枚举2k~k+1,强行将每一项的系数化为0,并操作1到k的系数使得式子恒等。这个复杂度是 O ( k 2 ) O(k^2) O(k2)的。
这好像多项式取模啊,能不能找出一个多项式,使得将系数多项式模这个多项式,就是直接对所有系数完成这一部分操作呢?
事实上,这个多项式就是
x
k
+
1
−
f
(
k
)
x
k
−
f
(
k
−
1
)
x
k
−
1
.
.
.
−
f
(
1
)
x
x^{k+1}-f(k)x^k-f(k-1)x^{k-1}...-f(1)x
xk+1−f(k)xk−f(k−1)xk−1...−f(1)x.
f
(
1
)
,
f
(
2
)
,
f
(
3
)
.
.
.
f
(
k
)
f(1),f(2),f(3)...f(k)
f(1),f(2),f(3)...f(k)是
g
k
+
1
g_{k+1}
gk+1的表示系数。
这似乎叫做这个线性递推的特征多项式。
总结
整理一下思路:
若得知g(n)的表达系数,则可以通过以下过程得g(2n)的表达系数:
- 将表达系数多项式平方,使用FFT加速。 O ( k log k ) O(k \log k) O(klogk)
- 将求得的多项式对特征多项式求模。 O ( k log k ) O(k \log k) O(klogk)
因此,整体复杂度也是
O
(
k
log
k
)
O(k \log k)
O(klogk)。要求得
g
x
g_x
gx,只需要从
g
1
g_1
g1开始倍增。
复杂度
O
(
k
log
k
log
n
)
O(k \log k \log n)
O(klogklogn)。
当然你可以把上述过程写成一个g(n)与g(m)合并,得出g(n+m)的函数。这样在求g(2n+1)时,只需要额外把g(2n)与g(1)合并即可。
代码细节很多,打前深思熟虑。
常数优化
待更