多项式求逆,多项式取模,多项式开方 学习笔记

前言

还记得上个学期tututu跟我提过多项式的很多操作,还有一些优化常数的奇技淫巧,然而那个时候我一脸懵逼。最近几天无所事事,去洛谷做比赛又整天被吊着打,闲暇之余就想着学一下多项式的几个基本操作。

其实一开始我是想学CZT的,根据myy的论文它能把BZOJ3992那题优化到 O(mlog(m)+mlog(n)) O ( m log ⁡ ( m ) + m log ⁡ ( n ) ) 。然而它的应用面不广我就很功利地没有学。还有如何用两次DFT求实序列的卷积等等。至于多项式牛顿迭代法,生成函数之类的,等我补完高数再回来学吧。说不定以后会填这些坑

多项式求逆,取模及开方应该算是多项式最为常见的应用,主要配合生成函数,常系数线性齐次递推优化一起用。虽然已经有很多dalao们写过详细的笔记,不过这些算法的题目比较少,我怕太久没写就忘了,所以还是自己写(shui)一篇QAQ。


多项式求逆:

定义:

给出多项式 A(x) A ( x ) ,现要求一个多项式 B(x) B ( x ) ,使得:

A(x)B(x)1(modxn) A ( x ) B ( x ) ≡ 1 ( mod x n )

为什么要模 xn x n ?如果不在模 xn x n 意义下定义逆元,除非 A(x) A ( x ) 只有常数项,否则根据二项式定理, B(x) B ( x ) 有无穷多项。

xn x n ,换句话说就是把多项式 A(x)B(x) A ( x ) B ( x ) 的n次方及更高次项截断。

暴力:

考虑如何暴力求逆。

C(x)=A(x)B(x) C ( x ) = A ( x ) ∗ B ( x ) 。由于 A0B0=C0=1 A 0 B 0 = C 0 = 1 ,可得 B0 B 0 A0 A 0 的逆元。又因为 A0B1+A1B0=C1=0 A 0 B 1 + A 1 B 0 = C 1 = 0 ,可以解得 B1=A1B0A0 B 1 = − A 1 B 0 A 0 。依此类推,即可解出 B0 B 0 Bn1 B n − 1 。解到 Bn1 B n − 1 即可停止,因为求逆在模 xn x n 意义下进行。

同时我们可以看出,多项式 A(x) A ( x ) 是否存在逆元 B(x) B ( x ) ,只取决于 A0 A 0 是否有逆元,因为每次求 Bi B i 的时候,分母的位置总是 A0 A 0 。在下面 O(nlog(n)) O ( n log ⁡ ( n ) ) 的算法中,同样可以证明这一点。

倍增:

为了方便,下面假设 n=2k(k>0) n = 2 k ( k > 0 ) 。如果实际操作中n不是2的幂,像FFT那样将n强行增大到2的幂即可。如果n=1,直接求 A0 A 0 的逆元即可。

假设已经求出多项式 G(x) G ( x ) ,使得:

A(x)G(x)1(modxn2) A ( x ) G ( x ) ≡ 1 ( mod x n 2 )

因为 A(x)B(x)1(modxn) A ( x ) B ( x ) ≡ 1 ( mod x n ) ,所以 A(x)B(x)1(modxn2) A ( x ) B ( x ) ≡ 1 ( mod x n 2 ) 。两式相减可得:

A(x)(G(x)B(x))0(modxn2) A ( x ) ( G ( x ) − B ( x ) ) ≡ 0 ( mod x n 2 )

因为 A(x) A ( x ) 不为0,所以 G(x)B(x)0(modxn2) G ( x ) − B ( x ) ≡ 0 ( mod x n 2 ) ,将其平方可得:

G2(x)+B2(x)2G(x)B(x)0(modxn) G 2 ( x ) + B 2 ( x ) − 2 G ( x ) B ( x ) ≡ 0 ( mod x n )

为什么式子最后面 xn2 x n 2 变成了 xn x n 呢?考虑 (G(x)B(x))2[i](0<=i<n) ( G ( x ) − B ( x ) ) 2 [ i ] ( 0 <= i < n ) ,它等于 ij=0(G(x)B(x))[j](G(x)B(x))[ij] ∑ j = 0 i ( G ( x ) − B ( x ) ) [ j ] ∗ ( G ( x ) − B ( x ) ) [ i − j ] ,而 G(x)B(x) G ( x ) − B ( x ) 的第 i i 项和第 ij i − j 项中至少有一项为0。

将式子两边同时乘以 A(x) A ( x ) ,可得:

A(x)G2(x)+B(x)2G(x)0(modxn) A ( x ) G 2 ( x ) + B ( x ) − 2 G ( x ) ≡ 0 ( mod x n )

(注意 A(x)B(x)1(modxn) A ( x ) B ( x ) ≡ 1 ( mod x n ) ,乘上去之后抵消了)

所以:

B(x)2G(x)A(x)G2(x)(modxn) B ( x ) ≡ 2 G ( x ) − A ( x ) G 2 ( x ) ( mod x n )

A(x) A ( x ) G(x) G ( x ) 做DFT,用A的对应值乘以G对应值的平方求出B的值,再做IDFT,求出 B(x) B ( x ) 的时间复杂度即为 O(nlog(n)) O ( n log ⁡ ( n ) ) ,常数是DFT的3倍。

总时间 T(n)=T(n2)+O(nlog(n))=O(nlog(n)) T ( n ) = T ( n 2 ) + O ( n log ⁡ ( n ) ) = O ( n log ⁡ ( n ) ) ,常数是DFT的6倍。

参考文章:

Miskcoo
SemiWaker

例题:

一道模板题:洛谷P4238
例题1:BZOJ3456
例题2:由于某种原因暂时还没有例题2


多项式取模:

定义:

A(x)=B(x)C(x)+D(x) A ( x ) = B ( x ) C ( x ) + D ( x ) ,其中 A(x) A ( x ) 的次数界为n, B(x) B ( x ) 的次数界为m (m<=n) ( m <= n ) C(x) C ( x ) 的次数界为n-m+1, D(x) D ( x ) 的次数界严格小于m。称 D(x) D ( x ) A(x) A ( x ) B(x) B ( x ) 的结果。给出 A(x) A ( x ) B(x) B ( x ) ,求 D(x) D ( x )

做法:

我们可以先求出 C(x) C ( x ) ,然后用一次FFT即可求出 D(x) D ( x )

考虑多项式除法和多项式逆元的相同特点。多项式除法是从高位到低位做,当前余式的次数界小于m便停止;而多项式逆元是从低位到高位做,一直做到第n-1位。前者忽略掉了当前式的0~m-2位,后者忽略掉了当前式的第n及以上位。

于是不妨将系数翻转。将 A(x),D(x) A ( x ) , D ( x ) 以界为n翻转, B(x) B ( x ) 以界为m, C(x) C ( x ) 以界为n-m+1翻转。记 AR(x)=n1i=0ani1xi A R ( x ) = ∑ i = 0 n − 1 a n − i − 1 x i ,则很明显有:

AR(x)=BR(x)CR(x)+DR(x) A R ( x ) = B R ( x ) C R ( x ) + D R ( x )

由于 D(x) D ( x ) 的最高项至多为 xm2 x m − 2 ,所以 DR(x) D R ( x ) 的最低项至少为 xnm+1 x n − m + 1 。因此可以列出式子:

AR(x)BR(x)CR(x)(modxnm+1) A R ( x ) ≡ B R ( x ) C R ( x ) ( mod x n − m + 1 )

这样我们就成功将翻转后的余式 DR(x) D R ( x ) 消掉了。又因为 CR(x) C R ( x ) 的界为n-m+1,模 xnm+1 x n − m + 1 无影响,可以求出翻转后的商式 CR(x) C R ( x )

CR(x)=AR(x)(BR(x))1(modxnm+1) C R ( x ) = A R ( x ) ∗ ( B R ( x ) ) − 1 ( mod x n − m + 1 )

总共只需一次多项式求逆和一次FFT即可。最后求出 D(x) D ( x ) 的时间复杂度依然为 O(nlog(n)) O ( n log ⁡ ( n ) ) 。假设一次FFT需要做两次DFT,那么多项式取模的常数大概是DFT的10倍。

参考文章:

SemiWaker

例题:

多项式取模优化常系数线性齐次递推:BZOJ4161


多项式开方:

定义:

给定多项式 A(x) A ( x ) ,要求出一个多项式 B(x) B ( x ) ,使得:

B2(x)A(x)(modxn) B 2 ( x ) ≡ A ( x ) ( mod x n )

倍增:

Q:为什么直接说倍增的方法?
A:因为这无法暴力。

同样地,假设我们已经求出 G(x) G ( x ) 使得 G2(x)A(x)(modxn2) G 2 ( x ) ≡ A ( x ) ( mod x n 2 ) ,则有:

G2(x)B2(x)0(modxn2) G 2 ( x ) − B 2 ( x ) ≡ 0 ( mod x n 2 )

(G(x)+B(x))(G(x)B(x))0(modxn2) ( G ( x ) + B ( x ) ) ( G ( x ) − B ( x ) ) ≡ 0 ( mod x n 2 )

因为是平方,所以会有两个解。我们不妨令 G(x)B(x)0(modxn2) G ( x ) − B ( x ) ≡ 0 ( mod x n 2 ) ,平方后则有:

G2(x)+B2(x)2G(x)B(x)0(modxn) G 2 ( x ) + B 2 ( x ) − 2 G ( x ) B ( x ) ≡ 0 ( mod x n )

因为 B2(x)A(x)(modxn) B 2 ( x ) ≡ A ( x ) ( mod x n ) ,所以:

B(x)A(x)+G2(x)2G(x)(modxn) B ( x ) ≡ A ( x ) + G 2 ( x ) 2 G ( x ) ( mod x n )

每倍增一次需要一次多项式求逆和一次FFT。时间复杂度依旧为 T(n)=T(n2)+O(nlog(n))=O(nlog(n)) T ( n ) = T ( n 2 ) + O ( n log ⁡ ( n ) ) = O ( n log ⁡ ( n ) ) 。如果在多项式求逆的最后一步将 G(x) G ( x ) DFT后的结果用于分母求 G2(x) G 2 ( x ) ,常数就大概是DFT的14倍。已经接近O(n^2)了QAQ

最后说一下关于n=1时,常数项开根的问题。如果是实数意义下的开方,直接做就可以。如果是模意义下的开方,需要用到一种叫做二次剩余的很厉害的算法,然而我已经懒得再动脑所以没学,选择了直接暴力枚举。模意义下是有可能没有平方根的。正如tututu之前整天对着懵逼的我说:“多项式开方的时间主要在常数项的开根,多项式是否能开方主要取决于常数项是否能开方。”

参考文章:

_zwl

例题:

生成函数相关:BZOJ3625


上面这些例题都是些最基本的套路题,然而其实我并没有刷完QAQ

你可能感兴趣的:(FFT-NTT,学习笔记)