打算开 GF 这个万恶之源了,但在此之前先把多项式的那堆板子理理清楚吧。代码没有刻意卡常,而且写成的年代不同,码风和实现方法会有一点不一样,板子也不会太全,之后会遇到问题会在这里慢慢补充。
多项式乘法
给定一个 n n 次多项式 F ( x ) = ∑ n i = 0 f i x i F ( x ) = ∑ i = 0 n f i x i 和 m m 次多项式 G ( x ) = ∑ m j = 0 g j x j G ( x ) = ∑ j = 0 m g j x j ,求出 F ( x ) F ( x ) 和 G ( x ) G ( x ) 的卷积:
n ∑ i = 0 m ∑ j = 0 f i g j x i + j ∑ i = 0 n ∑ j = 0 m f i g j x i + j
注意到多项式乘法的过程,单从卷积定义角度考虑是难以优化的,所以我们从多项式本身考虑。
多项式有两种表示方法,点值表示法和系数表示法。其中系数表示法就是我们常用的形式:
F ( x ) = { f 0 , f 1 , f 2 , ⋅ ⋅ ⋅ , f n } F ( x ) = { f 0 , f 1 , f 2 , ⋅ ⋅ ⋅ , f n }
其中 f i f i 表示 x i x i 前面的系数。这显然可以唯一确定一个 n n 次多项式。而点值表示法是这样的:
F ( x ) = { ( x 0 , F ( x 0 ) ) , ( x 1 , F ( x 1 ) ) , ⋅ ⋅ ⋅ , ( x n , F ( x n ) ) } F ( x ) = { ( x 0 , F ( x 0 ) ) , ( x 1 , F ( x 1 ) ) , ⋅ ⋅ ⋅ , ( x n , F ( x n ) ) }
注意到这 n + 1 n + 1 个点也能表示一个 n n 次多项式,可以从高斯消元的角度考虑。
显然我们常用的(题目输入和要求输出的)都是第一种表示方法,那第二种表示方法有啥用呢。对于两个 n n 次多项式 F ( x ) , G ( x ) F ( x ) , G ( x ) ,它们的卷积 H ( x ) H ( x ) 在点值表示法下能很轻易地被求出:
H ( x ) = { ( x 0 , F ( x 0 ) G ( x 0 ) ) , ( x 1 , F ( x 1 ) G ( x 1 ) ) , ⋅ ⋅ ⋅ , ( x n , F ( x n ) G ( x n ) ) } H ( x ) = { ( x 0 , F ( x 0 ) G ( x 0 ) ) , ( x 1 , F ( x 1 ) G ( x 1 ) ) , ⋅ ⋅ ⋅ , ( x n , F ( x n ) G ( x n ) ) }
时间复杂度仅有 O ( n ) O ( n ) 。但问题是题目不认点值表示法,而暴力在两种表示方法之间的转化复杂度是很高的。所以我们现在的任务就是找到在两种表示方法之间转化的合适方法。
FFT/快速傅里叶变换
前置知识:复数。
可以做到在 O ( n log n ) O ( n log n ) 的时间复杂下完成点值表示法和系数表示法的相互转化。
我们先来看从系数表示法到点值表示法,注意到这里的瓶颈在于算 x i x i 和计算 F ( x i ) F ( x i ) 。这两个问题本质上其实挺像的,都是要求寻找一个合适的 x x ,使得 x i x i 具有一些美妙的性质。
注意到 1 , − 1 1 , − 1 的幂都是很好算的,但这样仅仅够我们算两个点,一共可是需要 n + 1 n + 1 个。所以我们要更多的点,更具体地讲,我们需要更多满足 | ω k | = 1 | ω k | = 1 的 ω ω 。可以发现需要引入复数了,i , − i i , − i 显然是方程的解,除此之外,在单位圆上所有向量对应的复数都满足:
(应该都能看出来是从 OI wiki 拿的图吧)
严谨讲,我们定义 x n = 1 x n = 1 在 C C 中的解是 n n 次复根。根据上图,我们能发现这样的解有 n n 个,根据欧拉公式 e i x = cos x + i sin x e i x = cos x + i sin x ,我们定义:
ω n = e 2 π i n ω n = e 2 π i n
为单位复根,可以发现这个复数对应了把单位圆 n n 等分的第一个角对应的向量。则 x n = 1 x n = 1 的解集能用 w n w n 的幂表示:
{ ω k n | k ∈ [ 0 , n ) ∩ Z } { ω n k | k ∈ [ 0 , n ) ∩ Z }
说了这么多,真正要投入应用的话,我们还需要知道单位复根的一些性质。对于任意的正整数 n n 和整数 k k ,有:
ω n n = 1 , w k n = ω 2 k 2 n , w k + n 2 n = − w k 2 n ω n n = 1 , w n k = ω 2 n 2 k , w 2 n k + n = − w 2 n k
均可以通过把复数看成向量,用几何意义证明,不再赘述。而有了这些性质,就可以开始进入正片了。
FFT 的基本思路是分治,递归处理当 x = w k n x = w n k 时,f ( x ) f ( x ) 的值。我们来举个例子吧,8 8 项的多项式:
f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋅ ⋅ ⋅ + a 7 x 7 f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋅ ⋅ ⋅ + a 7 x 7
考虑奇偶分治,把式子按照下标的奇偶性分成两坨:
f ( x ) = ( a 0 + a 2 x 2 + a 4 x 4 + a 6 x 6 ) + ( a 1 x 1 + a 3 x 3 + a 5 x 5 + a 7 x 7 ) = ( a 0 + a 2 x 2 + a 4 x 4 + a 6 x 6 ) + x ( a 1 + a 3 x 2 + a 5 x 4 + a 7 x 6 ) f ( x ) = ( a 0 + a 2 x 2 + a 4 x 4 + a 6 x 6 ) + ( a 1 x 1 + a 3 x 3 + a 5 x 5 + a 7 x 7 ) = ( a 0 + a 2 x 2 + a 4 x 4 + a 6 x 6 ) + x ( a 1 + a 3 x 2 + a 5 x 4 + a 7 x 6 )
分别对奇偶项建立新函数:
g ( x ) = a 0 + a 2 x + a 4 x 2 + a 6 x 3 , h ( x ) = a 1 + a 3 x + a 5 x 2 + a 7 x 3 g ( x ) = a 0 + a 2 x + a 4 x 2 + a 6 x 3 , h ( x ) = a 1 + a 3 x + a 5 x 2 + a 7 x 3
则显然有:
f ( x ) = g ( x 2 ) + x h ( x 2 ) f ( x ) = g ( x 2 ) + x h ( x 2 )
好了,现在就可以代入 ω k n ω n k 了:
f ( ω k n ) = g ( ( ω k n ) 2 ) + ω k n h ( ( ω k n ) 2 ) = g ( ω 2 k n ) + ω k n h ( ω 2 k n ) = g ( ω k n 2 ) + ω k n h ( ω k n 2 ) f ( ω n k ) = g ( ( ω n k ) 2 ) + ω n k h ( ( ω n k ) 2 ) = g ( ω n 2 k ) + ω n k h ( ω n 2 k ) = g ( ω n 2 k ) + ω n k h ( ω n 2 k )
好像还是看不出来什么,再代入个 ω k + n 2 n ω n k + n 2 试试?推导过程省略,跟上面差不多,我们能得到:
f ( ω k + n 2 n ) = g ( w k n 2 ) − ω k n h ( ω k n 2 ) f ( ω n k + n 2 ) = g ( w n 2 k ) − ω n k h ( ω n 2 k )
好了,现在我们就完全能看出来了,求出 g ( ω k n 2 ) , h ( ω k n 2 ) g ( ω n 2 k ) , h ( ω n 2 k ) 之后我们就能一次处理处两个函数值!而处理这俩函数的过程又是一次递归的过程!这下找到了方向了,我们对于每个函数值,需要代入 n n 个不同的值,每次会把多项式长度缩减 1 2 1 2 ,所以最终复杂度就是 O ( n log n ) O ( n log n ) 。
值得注意的是,分治的时候需要保证每次分治两边长短相同,换句话说,需要 n = 2 m , m ∈ N n = 2 m , m ∈ N 。如果原多项式不满足的话,就用 0 0 补就好了。
好了,现在我们能把系数表示法换成点值表示法了,愉快地把函数值相乘,然后呢?然后我们就需要把点值表示法换成系数表示法了。这一过程通常被称为 IFFT,即逆 FFT。
考虑原本的多项式是 f ( x ) = ∑ n − 1 i = 0 a i x i f ( x ) = ∑ i = 0 n − 1 a i x i ,而现在我们已知 y i = f ( ω i n ) , i ∈ [ 0 , n ) ∩ Z y i = f ( ω n i ) , i ∈ [ 0 , n ) ∩ Z ,现在要求 { a 0 , a 1 , ⋅ ⋅ ⋅ , a n − 1 } { a 0 , a 1 , ⋅ ⋅ ⋅ , a n − 1 } 。考虑设:
A ( x ) = n − 1 ∑ i = 0 y i x i A ( x ) = ∑ i = 0 n − 1 y i x i
现在我们就要在 A ( x ) A ( x ) 上面搞搞事了。
考虑将 ω − i n ω n − i 分别代入 A ( x ) A ( x ) ,则有:
A ( ω − k n ) = n − 1 ∑ i = 0 f ( ω i n ) ω − i k n = n − 1 ∑ i = 0 ω − i k n n − 1 ∑ j = 0 a j ω i j = n − 1 ∑ i = 0 n − 1 ∑ j = 0 a j ω i ( j − k ) = n − 1 ∑ j = 0 a j n − 1 ∑ i = 0 ω i ( j − k ) A ( ω n − k ) = ∑ i = 0 n − 1 f ( ω n i ) ω n − i k = ∑ i = 0 n − 1 ω n − i k ∑ j = 0 n − 1 a j ω i j = ∑ i = 0 n − 1 ∑ j = 0 n − 1 a j ω i ( j − k ) = ∑ j = 0 n − 1 a j ∑ i = 0 n − 1 ω i ( j − k )
设 S ( ω a n ) = ∑ n − 1 i = 0 ( ω a i n ) S ( ω n a ) = ∑ i = 0 n − 1 ( ω n a i ) ,则当 a ≡ 0 ( mod n ) a ≡ 0 ( mod n ) 时,显然 S ( ω a n ) = n S ( ω n a ) = n (因为 ω a n = 1 ω n a = 1 嘛)。
而当 a ≢ 0 ( mod n ) a ≢ 0 ( mod n ) 时,考虑经典 trick 错位相减:
S ( ω a n ) = n − 1 ∑ i = 0 ( ω a i n ) ω a n S ( ω a n ) = n ∑ i = 1 ( ω a i n ) ∴ S ( ω a n ) = ω a n n − ω a 0 n ω a n − 1 = 0 S ( ω n a ) = ∑ i = 0 n − 1 ( ω n a i ) ω n a S ( ω n a ) = ∑ i = 1 n ( ω n a i ) ∴ S ( ω n a ) = ω n a n − ω n a 0 ω n a − 1 = 0
综上,我们有:
S ( ω a n ) = { n a ≡ 0 ( mod n ) 0 a ≢ 0 ( mod n ) S ( ω n a ) = { n a ≡ 0 ( mod n ) 0 a ≢ 0 ( mod n )
带回原式:
A ( ω − k n ) = n − 1 ∑ j = 0 a j S ( ω j − k n ) = a k n A ( ω n − k ) = ∑ j = 0 n − 1 a j S ( ω n j − k ) = a k n
非常神奇,如果令 b k = ω − k n b k = ω n − k ,则 A A 的点值表示法就是:
{ ( b 0 , a 0 n ) , ( b 1 , a 1 n ) , ⋅ ⋅ ⋅ , ( b n − 1 , a n − 1 n ) } { ( b 0 , a 0 n ) , ( b 1 , a 1 n ) , ⋅ ⋅ ⋅ , ( b n − 1 , a n − 1 n ) }
综上,我们取单位根为其倒数,然后对 A A 做一遍上述的 FFT 过程,再除以 n n 就得到了系数表示法。
递归版实现:c o d e c o d e
好的现在您写完了递归版,非常愉快地交了模板题,非常愉快地获得了 100 p t s 100 p t s ,但这一切快乐都在您打开提交记录,按照运行时间排序后结束了,“他们的为啥跑的这么快??”
直觉告诉我们是递归的锅。递归带来了大常数,让本就因为实数运算常数不小的 FFT 雪上加霜。所以我们要考虑把它变成非递归版本。
考虑模拟算法中递归分治的过程:
{ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } { 0 , 2 , 4 , 6 } { 1 , 3 , 5 , 7 } { 0 , 4 } { 2 , 6 } { 1 , 5 } { 3 , 7 } { 0 } { 4 } { 2 } { 6 } { 1 } { 5 } { 3 } { 7 } { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } { 0 , 2 , 4 , 6 } { 1 , 3 , 5 , 7 } { 0 , 4 } { 2 , 6 } { 1 , 5 } { 3 , 7 } { 0 } { 4 } { 2 } { 6 } { 1 } { 5 } { 3 } { 7 }
分完了。看不出来什么?考虑把分治前的数和分治后的数都变成二进制表示:
{ 000 , 001 , 010 , 011 , 100 , 101 , 110 , 111 } { 000 , 100 , 010 , 110 , 001 , 101 , 011 , 111 } { 000 , 001 , 010 , 011 , 100 , 101 , 110 , 111 } { 000 , 100 , 010 , 110 , 001 , 101 , 011 , 111 }
这下明显了吧,分治前后的数二进制位是反过来的!那我们只需要提前把数交换到对应的位置,然后模拟递归的过程就好了。
现在的问题是怎么把二进制位给反过来。O ( n log n ) O ( n log n ) 固然是一种方法,不过我们能做到 O ( n ) O ( n ) 。考虑递推,设 r e v x r e v x 表示 x x 的二进制位反过来的结果,显然有 r e v 0 = 0 r e v 0 = 0 。而对于一个数 x x ,如果除去最高位不算,r e v x = ⌊ r e v ⌊ x 2 ⌋ 2 ⌋ r e v x = ⌊ r e v ⌊ x 2 ⌋ 2 ⌋ ,也就是把 x x 右移一位的数反转一下再右移一位。而对于最高位,它取决于 x x 的奇偶性,所以有:
r e v x = ⌊ r e v ⌊ x 2 ⌋ 2 ⌋ + ( x mod 2 ) 2 k − 1 r e v x = ⌊ r e v ⌊ x 2 ⌋ 2 ⌋ + ( x mod 2 ) 2 k − 1
其中 k k 是二进制表示下的位数。
非递归版实现:c o d e c o d e
NTT/快速数论变换
前置知识:原根
刚刚我们看到了 FFT,这里还有一种思路类似的 NTT,是 FFT 在数论基础上的实现。由于 FFT 涉及到大量的实数运算,丢精度,速度慢就成为了不可避免的问题。而 NTT 是实现在数论基础上的,运算都是整数,准确度和速度都会更快,但时间复杂度依然是 O ( n log n ) O ( n log n ) 。
这俩的基本思路都差不多,而我们的关键就是在数论领域中找出一个东西来代替单位复根。我们发现,对于质数 p = q n + 1 ( n = 2 m ) p = q n + 1 ( n = 2 m ) ,它的原根 g g 恰好就满足刚刚所说的性质:
g q n ≡ 1 ( mod p ) g q n ≡ 1 ( mod p )
而如果我们把 g q g q 记作 g n g n ,会发现它也有类似的性质,如:
g n n ≡ 1 ( mod p ) , g n 2 n ≡ − 1 ( mod p ) g n n ≡ 1 ( mod p ) , g n n 2 ≡ − 1 ( mod p )
然后就结束了,只需要把 FFT 里的单位复根扣掉换成原根就行了。常见的质数原根:
p = 998 , 244 , 353 = 7 × 17 × 2 23 + 1 , g = 3 p = 998 , 244 , 353 = 7 × 17 × 2 23 + 1 , g = 3
其余的质数可以参考求原根的方法考场现求。
实现:c o d e c o d e
分治 FFT/NTT
给出序列 g 1 ∼ n g 1 ∼ n ,求出序列 f 0 ∼ n f 0 ∼ n ,其中:
f 0 = 1 , f i = i ∑ j = 1 f i − j g j f 0 = 1 , f i = ∑ j = 1 i f i − j g j
答案对 998 , 244 , 353 998 , 244 , 353 取模。
能看到很明显的卷积特征,但很遗憾,我们不能直接进行一个积的卷,因为对于 f i f i 来说,它的值是依赖以前求出来的值的,换句话说,我们要求在线的卷积。(这也是为什么这个算法在国外有时也被称为在线卷积)
考虑参考 c d q c d q 分治的思路,递归处理区间。先处理左区间,然后处理左区间对有区间的贡献,最后处理右区间,这样能保证每次卷积需要的值都已经被求出了。
具体来讲,对于当前区间 [ l , r ) [ l , r ) ,我们先递归求出 [ l , m i d ) [ l , m i d ) 中 f f 的值,然后将 f l ∼ m i d − 1 f l ∼ m i d − 1 与 g 0 ∼ r − l − 1 g 0 ∼ r − l − 1 给卷起来,这样能得到左边对右边 f m i d , r − 1 f m i d , r − 1 的贡献。之后递归处理右区间 [ m i d , r ) [ m i d , r ) 即可。时间复杂度:
T ( n ) = 2 T ( n 2 ) + O ( n log n ) = O ( n log 2 n ) T ( n ) = 2 T ( n 2 ) + O ( n log n ) = O ( n log 2 n )
实现:c o d e c o d e
注意卷积之前要补 0 0 清空而不是不管!
MTT
任意模数 NTT,不会。
多项式牛顿迭代
给出多项式 g ( x ) g ( x ) ,已知存在一个多项式 f ( x ) f ( x ) ,满足:
g ( f ( x ) ) ≡ 0 ( mod x n ) g ( f ( x ) ) ≡ 0 ( mod x n )
求出模 x n x n 意义下的 f ( x ) f ( x ) 。
考虑倍增的思路。首先考虑边界,对于 n = 1 n = 1 的情况,我们需要单独求出 [ x 0 ] g ( f ( x ) ) = 0 [ x 0 ] g ( f ( x ) ) = 0 的解。然后对于其余的 n n ,我们考虑先递归计算 ⌈ n 2 ⌉ ⌈ n 2 ⌉ 的情况,假设得到在模 x ⌈ n 2 ⌉ x ⌈ n 2 ⌉ 意义下的解是 f 0 ( x ) f 0 ( x ) ,则现在我们的目标就是要用 f 0 ( x ) f 0 ( x ) 表示 f ( x ) f ( x ) 。
考虑将 g ( f ( x ) ) g ( f ( x ) ) 在 f 0 ( x ) f 0 ( x ) 处泰勒展开,则原同余方程可化为:
∑ i ≥ 0 g ( i ) ( f 0 ( x ) ) i ! ( f ( x ) − f 0 ( x ) ) i ≡ 0 ( mod x n ) ∑ i ≥ 0 g ( i ) ( f 0 ( x ) ) i ! ( f ( x ) − f 0 ( x ) ) i ≡ 0 ( mod x n )
其中 g ( i ) g ( i ) 表示 g g 的 i i 阶导。注意到 f ( x ) − f 0 ( x ) f ( x ) − f 0 ( x ) 这个式子的最低非 0 0 项次数最低是 ⌈ n 2 ⌉ ⌈ n 2 ⌉ ,因为在这之前它们都一样,所以有:
∀ i ≥ 2 : ( f ( x ) − f 0 ( x ) ) i ≡ 0 ( mod x n ) ∀ i ≥ 2 : ( f ( x ) − f 0 ( x ) ) i ≡ 0 ( mod x n )
这样,原同余方程又能化为:
g ( f 0 ( x ) ) + g ′ ( f 0 ( x ) ) ( f ( x ) − f 0 ( x ) ) ≡ 0 ( mod x n ) g ( f 0 ( x ) ) + g ′ ( f 0 ( x ) ) ( f ( x ) − f 0 ( x ) ) ≡ 0 ( mod x n )
简单的代数变化:
f ( x ) ≡ f 0 ( x ) − g ( f 0 ( x ) ) g ′ ( f 0 ( x ) ) ( mod x n ) f ( x ) ≡ f 0 ( x ) − g ( f 0 ( x ) ) g ′ ( f 0 ( x ) ) ( mod x n )
这下式子有了,至于怎么求,怎么用,且听下文分解。
多项式求逆
给定一个多项式 f ( x ) f ( x ) ,求出一个多项式 g ( x ) g ( x ) 满足:
f ( x ) g ( x ) ≡ 1 ( mod x n ) f ( x ) g ( x ) ≡ 1 ( mod x n )
系数对 998 , 244 , 353 998 , 244 , 353 取模。
考虑套刚刚的多项式牛顿迭代。为了避免跟上文的符号矛盾,我们记待求逆的函数为 h ( x ) h ( x ) ,求逆结果函数为 f ( x ) f ( x ) ,则有:
g ( f ( x ) ) = 1 f ( x ) − h ( x ) ≡ 0 ( mod x n ) g ( f ( x ) ) = 1 f ( x ) − h ( x ) ≡ 0 ( mod x n )
套牛顿迭代的式子有:
f ( x ) ≡ f 0 ( x ) − 1 f 0 ( x ) − h ( x ) − 1 f 2 0 ( x ) ( mod x n ) ≡ f 0 ( x ) ( 2 − f 0 ( x ) h ( x ) ) ( mod x n ) f ( x ) ≡ f 0 ( x ) − 1 f 0 ( x ) − h ( x ) − 1 f 0 2 ( x ) ( mod x n ) ≡ f 0 ( x ) ( 2 − f 0 ( x ) h ( x ) ) ( mod x n )
注意这里求导的时候把 h ( x ) h ( x ) 当成常数来做了,然后就结束了,时间复杂度:
T ( n ) = T ( n 2 ) + O ( n log n ) = O ( n log n ) T ( n ) = T ( n 2 ) + O ( n log n ) = O ( n log n )
实现:c o d e c o d e
注意,在实现牛顿迭代的时候,类似分治 FFT,卷积之前一定要记得清空不用的!
多项式开根
给出一个 n − 1 n − 1 次多项式 A ( x ) A ( x ) ,找出一个模 x n x n 意义下的多项式 B ( x ) B ( x ) 使得
B 2 ( x ) ≡ A ( x ) ( mod x n ) B 2 ( x ) ≡ A ( x ) ( mod x n )
系数对 998 , 244 , 353 998 , 244 , 353 取模。
依然考虑牛顿迭代。类似求逆的推导过程,我们依然记 h ( x ) h ( x ) 为待开根函数,f ( x ) f ( x ) 为答案,则有:
g ( f ( x ) ) = f 2 ( x ) − h ( x ) ≡ 0 ( mod x n ) g ( f ( x ) ) = f 2 ( x ) − h ( x ) ≡ 0 ( mod x n )
还是套牛顿迭代的式子:
f ( x ) ≡ f 0 ( x ) − f 2 0 ( x ) − h ( x ) 2 f 0 ( x ) ( mod x n ) ≡ f 2 0 ( x ) + h ( x ) 2 f 0 ( x ) f ( x ) ≡ f 0 ( x ) − f 0 2 ( x ) − h ( x ) 2 f 0 ( x ) ( mod x n ) ≡ f 0 2 ( x ) + h ( x ) 2 f 0 ( x )
多项式求逆就可以做不带余数的除法了。类似求逆,时间复杂度 O ( n log n ) O ( n log n ) 。
实现:c o d e c o d e
多项式求导/积分
这个比较简单了,主要是一些公式。
首先由于求导和积分的线性性:
( f ( x ) + g ( x ) ) ′ = f ′ ( x ) + g ′ ( x ) ( c f ( x ) ) ′ = c f ′ ( x ) ∫ ( f ( x ) + g ( x ) ) = ∫ f ( x ) + ∫ g ( x ) ∫ c f ( x ) = c ∫ f ( x ) ( f ( x ) + g ( x ) ) ′ = f ′ ( x ) + g ′ ( x ) ( c f ( x ) ) ′ = c f ′ ( x ) ∫ ( f ( x ) + g ( x ) ) = ∫ f ( x ) + ∫ g ( x ) ∫ c f ( x ) = c ∫ f ( x )
所以对于多项式 F ( x ) = ∑ n − 1 i = 0 a i x i F ( x ) = ∑ i = 0 n − 1 a i x i ,它的导数和积分分别为:
F ′ ( x ) = n − 1 ∑ i = 1 a i i x i − 1 ∫ F ( x ) = n − 2 ∑ i = 0 a i x i + 1 i + 1 F ′ ( x ) = ∑ i = 1 n − 1 a i i x i − 1 ∫ F ( x ) = ∑ i = 0 n − 2 a i x i + 1 i + 1
可以 O ( n ) O ( n ) 求解。
还有一些比较常用的求导公式:
( F ( x ) G ( x ) ) ′ = F ′ ( x ) G ( x ) + F ( x ) G ′ ( x ) ( F ( x ) G ( x ) ) ′ = F ′ ( x ) G ( x ) − F ( x ) G ′ ( x ) G 2 ( x ) ( G ( F ( x ) ) ) ′ = G ′ ( F ( x ) ) F ′ ( x ) ( F ( x ) G ( x ) ) ′ = F ′ ( x ) G ( x ) + F ( x ) G ′ ( x ) ( F ( x ) G ( x ) ) ′ = F ′ ( x ) G ( x ) − F ( x ) G ′ ( x ) G 2 ( x ) ( G ( F ( x ) ) ) ′ = G ′ ( F ( x ) ) F ′ ( x )
更多的东西比如积分公式啊,更多的求导公式啊,常见的函数的积分导数啊,建议大概学一下微积分。
多项式带余除法
给出一个 n n 次多项式 F ( x ) F ( x ) 和一个 m m 次多项式 G ( x ) G ( x ) ,求出多项式 Q ( x ) , R ( x ) Q ( x ) , R ( x ) 满足以下条件:
Q ( x ) Q ( x ) 次数为 n − m n − m ,R ( x ) R ( x ) 次数小于 m m 。
F ( x ) = Q ( x ) G ( x ) + R ( x ) F ( x ) = Q ( x ) G ( x ) + R ( x ) 。
系数对 998 , 244 , 353 998 , 244 , 353 取模。
刚刚其实我们也做过分数运算(在牛顿迭代那一块),但这里不一样的是,题目还要求求出 R ( x ) R ( x ) ,即余数。这不太好办,所以考虑消去 R ( x ) R ( x ) 的影响。
有个很妙的思想 (不知道咋想到的) ,考虑构造多项式 F R ( x ) F R ( x ) :
F R ( x ) = x n F ( 1 x ) F R ( x ) = x n F ( 1 x )
容易想到 F R ( x ) F R ( x ) 的实质其实就把系数反转一下。顺着这个思路,将带余除法的等式两边同时乘上 x n x n 并把函数里的自变量都改为 1 x 1 x 有“
x n F ( 1 x ) = x n − m Q ( 1 x ) x m G ( 1 x ) + x n − m + 1 x m − 1 R ( 1 x ) F R ( x ) = Q R ( x ) G R ( x ) + x n − m + 1 R R ( x ) x n F ( 1 x ) = x n − m Q ( 1 x ) x m G ( 1 x ) + x n − m + 1 x m − 1 R ( 1 x ) F R ( x ) = Q R ( x ) G R ( x ) + x n − m + 1 R R ( x )
诶,这个式子就特殊了,注意到只有 R R ( x ) R R ( x ) 这一项前面有一个 x n − m + 1 x n − m + 1 ,那我们只需要把上式放在模 x n − m + 1 x n − m + 1 意义下不就把 R R ( x ) R R ( x ) 干掉了吗,即:
F R ( x ) ≡ Q R ( x ) G R ( x ) ( mod x n − m + 1 ) F R ( x ) ≡ Q R ( x ) G R ( x ) ( mod x n − m + 1 )
问题是,这会不会对 Q R ( x ) Q R ( x ) 造成影响导致我们求出的值不精确呢?显然不会,因为 Q R ( x ) Q R ( x ) 的次数仅有 n − m n − m ,小于模数的 n − m + 1 n − m + 1 次,所以 Q R ( x ) Q R ( x ) 并不会受到影响。
这样求个逆就能把 Q ( x ) Q ( x ) 求出来了。求出来之后再反代回去就有 R ( x ) R ( x ) 了。时间复杂度 O ( n log n ) O ( n log n ) 。
实现:c o d e c o d e
多项式 ln
给出 n − 1 n − 1 次多项式 A ( x ) A ( x ) ,求一个模 x n x n 意义下的多项式 B ( x ) B ( x ) 满足:
B ( x ) ≡ ln A ( x ) ( mod x n ) B ( x ) ≡ ln A ( x ) ( mod x n )
系数对 998 , 244 , 353 998 , 244 , 353 取模。
考虑设 G ( x ) = ln x G ( x ) = ln x ,则原题相当于求 G ( F ( x ) ) G ( F ( x ) ) ,考虑给这玩意求个导:
( G ( F ( x ) ) ) ′ = G ′ ( F ( x ) ) F ′ ( x ) ( G ( F ( x ) ) ) ′ = G ′ ( F ( x ) ) F ′ ( x )
由于,
( ln x ) ′ = 1 x ( ln x ) ′ = 1 x
则上式为:
( G ( F ( x ) ) ) ′ = F ′ ( x ) F ( x ) ( G ( F ( x ) ) ) ′ = F ′ ( x ) F ( x )
这样我们就能求出带求多项式的导数了,之后只需要积分回来即可:
∫ F ′ ( x ) F ( x ) ∫ F ′ ( x ) F ( x )
这样直接求导+求逆+积分这题就做完了。时间复杂度 O ( n log n ) O ( n log n ) 。
实现:c o d e c o d e
多项式 Exp
给出 n − 1 n − 1 次多项式 A ( x ) A ( x ) ,求一个模 x n x n 意义下的多项式 B ( x ) B ( x ) 满足:
B ( x ) ≡ e A ( x ) ( mod x n ) B ( x ) ≡ e A ( x ) ( mod x n )
系数对 998 , 244 , 353 998 , 244 , 353 取模。
考虑多项式牛顿迭代法。记 A ( x ) A ( x ) 为 h ( x ) h ( x ) ,B ( x ) B ( x ) 为 f ( x ) f ( x ) ,则有:
g ( f ( x ) ) = ln f ( x ) − h ( x ) ≡ 0 ( mod x n ) g ( f ( x ) ) = ln f ( x ) − h ( x ) ≡ 0 ( mod x n )
套式子:
f ( x ) = f 0 ( x ) − ln f 0 ( x ) − h ( x ) 1 f 0 ( x ) = f 0 ( x ) ( 1 − ln f 0 ( x ) + h ( x ) ) f ( x ) = f 0 ( x ) − ln f 0 ( x ) − h ( x ) 1 f 0 ( x ) = f 0 ( x ) ( 1 − ln f 0 ( x ) + h ( x ) )
求个 ln ln ,再加加减减后跟其他的乘起来就完事了。时间复杂度 O ( n log n ) O ( n log n ) 。
实现:c o d e c o d e
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具