还记得上个学期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 ) ,使得:
为什么要模 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 至 Bn−1 B n − 1 。解到 Bn−1 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)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) A ( x ) 不为0,所以 G(x)−B(x)≡0(modxn2) G ( x ) − B ( x ) ≡ 0 ( mod x n 2 ) ,将其平方可得:
为什么式子最后面 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))[i−j] ∑ j = 0 i ( G ( x ) − B ( x ) ) [ j ] ∗ ( G ( x ) − B ( x ) ) [ i − j ] ,而 G(x)−B(x) G ( x ) − B ( x ) 的第 i i 项和第 i−j i − j 项中至少有一项为0。
将式子两边同时乘以 A(x) A ( x ) ,可得:
(注意 A(x)B(x)≡1(modxn) A ( x ) B ( x ) ≡ 1 ( 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)=∑n−1i=0an−i−1xi A R ( x ) = ∑ i = 0 n − 1 a n − i − 1 x i ,则很明显有:
由于 D(x) D ( x ) 的最高项至多为 xm−2 x m − 2 ,所以 DR(x) D R ( x ) 的最低项至少为 xn−m+1 x n − m + 1 。因此可以列出式子:
这样我们就成功将翻转后的余式 DR(x) D R ( x ) 消掉了。又因为 CR(x) C R ( x ) 的界为n-m+1,模 xn−m+1 x n − m + 1 无影响,可以求出翻转后的商式 CR(x) C R ( x ) :
总共只需一次多项式求逆和一次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 ) ,使得:
Q:为什么直接说倍增的方法?
A:因为这无法暴力。
同样地,假设我们已经求出 G(x) G ( x ) 使得 G2(x)≡A(x)(modxn2) G 2 ( x ) ≡ A ( x ) ( mod x n 2 ) ,则有:
因为是平方,所以会有两个解。我们不妨令 G(x)−B(x)≡0(modxn2) G ( x ) − B ( x ) ≡ 0 ( mod x n 2 ) ,平方后则有:
因为 B2(x)≡A(x)(modxn) B 2 ( x ) ≡ A ( 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