多项式算法5:多项式求逆
多项式求逆
这里的多项式求逆,其实是求多项式的逆元。
对于多项式
A
(
x
)
A(x)
A(x),如果存在
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
n
)
A(x)B(x) \equiv 1( \mod x^n )
A(x)B(x)≡1(modxn),那么我们称
B
(
x
)
B(x)
B(x)为
A
(
x
)
A(x)
A(x)在模
x
n
x^n
xn意义下的逆元。
这里的模
x
n
x^n
xn是指舍弃所有
x
x
x的次数大于等于
n
n
n的项,当然也可以理解为所有
x
x
x的次数大于等于
n
n
n的项的系数赋为零。
例如,多项式
x
4
+
4
x
3
+
6
x
2
+
4
x
+
1
x^4+4x^3+6x^2+4x+1
x4+4x3+6x2+4x+1对
x
3
x^3
x3取模后变为
6
x
2
+
4
x
+
1
6x^2+4x+1
6x2+4x+1。
当
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
n
)
A(x)B(x) \equiv 1( \mod x^n )
A(x)B(x)≡1(modxn),表示
A
(
x
)
A(x)
A(x)和
B
(
x
)
B(x)
B(x)在模
x
n
x^n
xn的意义下相乘得到的多项式,只有常数项为
1
1
1,其它项的系数均为
0
0
0。
小结论:如果满足
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
n
)
A(x)B(x) \equiv 1( \mod x^n )
A(x)B(x)≡1(modxn),那么对于
1
≤
m
<
n
1 \le m \lt n
1≤m<n,
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
m
)
A(x)B(x) \equiv 1( \mod x^m )
A(x)B(x)≡1(modxm)也成立。
证明:不难发现由于
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
n
)
A(x)B(x) \equiv 1( \mod x^n )
A(x)B(x)≡1(modxn),表示
A
(
x
)
B
(
x
)
A(x)B(x)
A(x)B(x)的多项式的
x
x
x的
1
1
1到
n
−
1
n-1
n−1次方项系数原本就均为
0
0
0,
x
x
x的
n
n
n次方及更高次项已被强行赋了一个系数
0
0
0。当模
x
m
(
1
≤
m
<
n
)
x^m(1 \le m \lt n)
xm(1≤m<n)时,
x
x
x的
m
m
m到
n
−
1
n-1
n−1次方项系数本来就为零,强行赋零后也不影响结果的正确性。
这相当于
3
%
5
3 \% 5
3%5和
3
%
6
3 \% 6
3%6结果一样。
下面就是推导时间了。
我们设
B
(
x
)
B(x)
B(x)为
A
(
x
)
A(x)
A(x)在模
x
n
x^n
xn意义下的逆元,
B
′
(
x
)
B'(x)
B′(x)为
A
(
x
)
A(x)
A(x)在模
x
⌈
n
2
⌉
x^{\lceil \frac{n}{2} \rceil}
x⌈2n⌉意义下的逆元,那么有:
A
(
x
)
−
1
≡
B
(
x
)
(
m
o
d
x
n
)
A(x)^{-1} \equiv B(x)( \mod x^n )
A(x)−1≡B(x)(modxn)
A
(
x
)
−
1
≡
B
′
(
x
)
(
m
o
d
x
⌈
n
2
⌉
)
A(x)^{-1} \equiv B'(x)( \mod x^{\lceil \frac{n}{2} \rceil} )
A(x)−1≡B′(x)(modx⌈2n⌉)得
B
(
x
)
≡
B
′
(
x
)
(
m
o
d
x
⌈
n
2
⌉
)
B(x) \equiv B'(x)( \mod x^{\lceil \frac{n}{2} \rceil} )
B(x)≡B′(x)(modx⌈2n⌉)
B
(
x
)
−
B
′
(
x
)
≡
0
(
m
o
d
x
⌈
n
2
⌉
)
B(x) - B'(x) \equiv 0( \mod x^{\lceil \frac{n}{2} \rceil} )
B(x)−B′(x)≡0(modx⌈2n⌉)前
⌈
n
2
⌉
−
1
\lceil \frac{n}{2} \rceil - 1
⌈2n⌉−1次项系数均为零,乘方后小于
n
−
1
n-1
n−1次的项一定有一边的系数来自前
⌈
n
2
⌉
−
1
\lceil \frac{n}{2} \rceil - 1
⌈2n⌉−1次项,系数也为
0
0
0
(
B
(
x
)
−
B
′
(
x
)
)
2
≡
0
(
m
o
d
x
n
)
(B(x) - B'(x))^2 \equiv 0( \mod x^{n} )
(B(x)−B′(x))2≡0(modxn)
B
(
x
)
2
−
2
B
(
x
)
B
′
(
x
)
+
B
′
(
x
)
2
≡
0
(
m
o
d
x
n
)
B(x)^2 - 2B(x)B'(x) + B'(x)^2 \equiv 0( \mod x^{n} )
B(x)2−2B(x)B′(x)+B′(x)2≡0(modxn)乘上
A
(
x
)
A(x)
A(x),有:
A
(
x
)
B
(
x
)
B
(
x
)
−
2
A
(
x
)
B
(
x
)
B
′
(
x
)
+
A
(
x
)
B
′
(
x
)
B
′
(
x
)
≡
0
(
m
o
d
x
n
)
A(x)B(x)B(x) - 2A(x)B(x)B'(x) + A(x)B'(x)B'(x) \equiv 0( \mod x^{n} )
A(x)B(x)B(x)−2A(x)B(x)B′(x)+A(x)B′(x)B′(x)≡0(modxn)因为
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
n
)
A(x)B(x) \equiv 1( \mod x^n )
A(x)B(x)≡1(modxn),所以有:
B
(
x
)
−
2
B
′
(
x
)
+
A
(
x
)
B
′
(
x
)
B
′
(
x
)
≡
0
(
m
o
d
x
n
)
B(x) - 2B'(x) + A(x)B'(x)B'(x) \equiv 0( \mod x^{n} )
B(x)−2B′(x)+A(x)B′(x)B′(x)≡0(modxn)
B
(
x
)
≡
2
B
′
(
x
)
−
A
(
x
)
B
′
(
x
)
B
′
(
x
)
(
m
o
d
x
n
)
B(x) \equiv 2B'(x) - A(x)B'(x)B'(x) ( \mod x^{n} )
B(x)≡2B′(x)−A(x)B′(x)B′(x)(modxn)
B
(
x
)
≡
B
′
(
x
)
(
2
−
A
(
x
)
B
′
(
x
)
)
(
m
o
d
x
n
)
B(x) \equiv B'(x)(2 - A(x)B'(x)) ( \mod x^{n} )
B(x)≡B′(x)(2−A(x)B′(x))(modxn)
这说明,我们可以通过多项式乘法用
B
′
(
x
)
B'(x)
B′(x)求出
B
(
x
)
B(x)
B(x),
n
=
1
n=1
n=1时直接求常数项逆元,倍增
n
n
n,从小往大即可,时间复杂度为
Θ
(
n
log
2
n
)
\varTheta(n \log^2n)
Θ(nlog2n)。
我们的模板题还要求取模,模数
998244353
998244353
998244353十分友好,故我们直接用NTT求解。
多项式求逆模板题
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;
const int N = 1 << 22;
const int g = 3 , gi = 332748118 , mod = 998244353;
ll qw( ll a , ll b ) {
ll ans = 1;
while ( b ) {
if( b & 1 ) {
ans = ans * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return ans;
}
int rev[N];
int n;
void pre( int bit ) {
for ( int i = 0 ; i < ( 1 << bit ) ; ++i ) {
rev[i] = (rev[i>>1]>>1)|((i&1)<<(bit - 1));
}
}
void NTT( ll *F , int len , int on ) {
for ( int i = 0 ; i < len ; ++i ) {
if ( i < rev[i] ) {
swap( F[i] , F[rev[i]] );
}
}
for ( int i = 2 ; i <= len ; i <<= 1 ) {
ll gn = qw( on ? g : gi , ( mod - 1 ) / ( i ) );
for ( int j = 0 ; j <= len - 1 ; j += i ) {
ll gg = 1;
for ( int k = j ; k < j + i / 2 ; ++k ) {
ll u = F[k];
ll v = gg * F[k + i / 2] % mod;
F[k] = (u + v) % mod;
F[k + i / 2] = ( u - v + mod ) % mod;
gg = gg * gn % mod;
}
}
}
return;
}
ll ta[N] , tb[N];
void solve( int len , ll *a , ll *b ) {
if( len == 1 ) {
b[0] = qw( a[0] , mod - 2 );
return;
}
solve( ( len + 1 ) >> 1 , a , b );
int l = 1;
int bit = 0;
while ( l <= len + n ) {
l <<= 1;
++bit;
}
pre( bit );
for ( int i = 0 ; i < l ; ++i ) {
ta[i] = a[i];
tb[i] = ( i < ( ( len + 1 ) >> 1 ) ? b[i] : 0 );
}
NTT( ta , l , 1 );
NTT( tb , l , 1 );
for ( int i = 0 ; i < l ; ++i ) {
ta[i] = tb[i] * ( ( ( 2 - ta[i] * tb[i] ) % mod + mod ) % mod ) % mod;
}
NTT( ta , l , 0 );
ll inv = qw( l , mod - 2 );
for ( int i = 0 ; i < len ; ++i ) {
b[i] = ta[i] * inv % mod;
}
}
ll a[N] , b[N];
int main(){
scanf("%d",&n);
for ( int i = 0 ; i < n ; ++i ) {
scanf("%lld",&a[i]);
}
solve( n , a , b );
for ( int i = 0 ; i < n ; ++i ) {
printf("%lld ",b[i]);
}
return 0;
}