多项式算法5:多项式求逆

本文详细介绍了多项式求逆的概念,通过模运算和快速傅立叶变换(FFT)的NTT版本来求解。重点在于如何利用逆元性质和NTT算法提高求解效率,适用于模x^n下的多项式运算,并提供了一个基于NTT的模板题实例。
摘要由CSDN通过智能技术生成

多项式算法5:多项式求逆

前置知识:
FFT
NTT

多项式求逆

这里的多项式求逆,其实是求多项式的逆元。
对于多项式 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 1m<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 n1次方项系数原本就均为 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(1m<n)时, x x x m m m n − 1 n-1 n1次方项系数本来就为零,强行赋零后也不影响结果的正确性。
这相当于 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} x2n意义下的逆元,那么有:
A ( x ) − 1 ≡ B ( x ) ( m o d    x n ) A(x)^{-1} \equiv B(x)( \mod x^n ) A(x)1B(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)1B(x)(modx2n) 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)(modx2n) 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(modx2n) ⌈ n 2 ⌉ − 1 \lceil \frac{n}{2} \rceil - 1 2n1次项系数均为零,乘方后小于 n − 1 n-1 n1次的项一定有一边的系数来自前 ⌈ n 2 ⌉ − 1 \lceil \frac{n}{2} \rceil - 1 2n1次项,系数也为 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))20(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)22B(x)B(x)+B(x)20(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)(2A(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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值