多项式算法7:多项式除法

多项式算法7:多项式除法

前置知识:
FFT
NTT
多项式求逆

多项式除法

算法简述:

给定多项式 F F F , G G G求出多项式 R R R Q Q Q使得 F = G × Q + R F=G \times Q + R F=G×Q+R
其中 F F F的次数为 n n n G G G的次数为 m m m,一般 n > m n \gt m n>m

首先,我们列出式子: F ( x ) = G ( x ) × Q ( x ) + R ( x ) F(x)=G(x) \times Q(x) + R(x) F(x)=G(x)×Q(x)+R(x)我们把 x x x换成 1 x \frac{1}{x} x1得: F ( 1 x ) = G ( 1 x ) × Q ( 1 x ) + R ( 1 x ) F(\frac{1}{x})=G(\frac{1}{x}) \times Q(\frac{1}{x}) + R(\frac{1}{x}) F(x1)=G(x1)×Q(x1)+R(x1)两边乘以 x n x^n xn可得: F ( 1 x ) x n = G ( 1 x ) x m × Q ( 1 x ) x n − m + R ( 1 x ) x n F(\frac{1}{x}) x^{n}=G(\frac{1}{x}) x^{m} \times Q(\frac{1}{x}) x^{n-m} + R(\frac{1}{x})x^{n} F(x1)xn=G(x1)xm×Q(x1)xnm+R(x1)xn F ( 1 x ) x n = G ( 1 x ) x m × Q ( 1 x ) x n − m + R ( 1 x ) x m − 1 × x n − m + 1 F(\frac{1}{x}) x^{n}=G(\frac{1}{x}) x^{m} \times Q(\frac{1}{x}) x^{n-m} + R(\frac{1}{x}) x^{m-1} \times x^{n-m+1} F(x1)xn=G(x1)xm×Q(x1)xnm+R(x1)xm1×xnm+1我们设 A r ( x ) = x n A ( 1 x ) A_r(x)=x^nA(\frac{1}{x}) Ar(x)=xnA(x1),不难发现,两个多项式满足 A r ( x ) A_r(x) Ar(x)的第 i i i项系数等于 A ( x ) A(x) A(x)的第 n − i n-i ni项系数。把 A ( x ) A(x) A(x)翻转一下就可以得到 A r ( x ) A_r(x) Ar(x)
故我们根据上面的式子可得: F r ( x ) = G r ( x ) × Q r ( x ) + R r ( x ) x n − m + 1 F_r(x)=G_r(x) \times Q_r(x) + R_r(x)x^{n-m+1} Fr(x)=Gr(x)×Qr(x)+Rr(x)xnm+1 F r ( x ) ≡ G r ( x ) × Q r ( x ) + R r ( x ) x n − m + 1 ( m o d    x n − m + 1 ) F_r(x) \equiv G_r(x) \times Q_r(x) + R_r(x)x^{n-m+1} (\mod x^{n-m+1} ) Fr(x)Gr(x)×Qr(x)+Rr(x)xnm+1(modxnm+1) F r ( x ) ≡ G r ( x ) × Q r ( x ) ( m o d    x n − m + 1 ) F_r(x) \equiv G_r(x) \times Q_r(x) (\mod x^{n-m+1} ) Fr(x)Gr(x)×Qr(x)(modxnm+1) Q r ( x ) ≡ F r ( x ) × G r − 1 ( x ) ( m o d    x n − m + 1 ) Q_r(x) \equiv F_r(x) \times G_r^{-1}(x) (\mod x^{n-m+1} ) Qr(x)Fr(x)×Gr1(x)(modxnm+1) G r ( x ) G_r(x) Gr(x)的逆,然后就可以利用多项式乘法求出 Q r ( x ) Q_r(x) Qr(x) Q ( x ) Q(x) Q(x),然后再 R ( x ) = F ( x ) − G ( x ) × Q ( x ) R(x)=F(x)-G(x) \times Q(x) R(x)=F(x)G(x)×Q(x)直接计算即可,时间复杂度 Θ ( n log ⁡ n ) \varTheta(n \log n) Θ(nlogn)

模板题
F r ( x ) × G r − 1 ( x ) F_r(x) \times G_r^{-1}(x) Fr(x)×Gr1(x)的运算要记得先把 n − m + 1 n-m+1 nm+1以及后面的项清零,否则不知为何会WA,这是我调了5个小时得到的教训

#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 , m;
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;
}
void mul( ll *a , ll *b , int bit ) {
	pre( bit );
	int len = ( 1 << bit );
	NTT( a , len , 1 );
	NTT( b , len , 1 );
	for ( int i = 0 ; i < len ; ++i ) {
		a[i] = a[i] * b[i] % mod;
	}
	NTT( a , len , 0 );
	ll inv = qw( (ll)len , mod - 2 );
	for ( int i = 0 ; i < len ; ++i ) {
		a[i] = a[i] * inv % mod;
	}
}
ll ta[N] , tb[N];
void getinv( int len , ll *a , ll *b ) {
	if( len == 1 ) {
		b[0] = qw( a[0] , mod - 2 );
		return;
	}
	getinv( ( len + 1 ) >> 1 , a , b );
	int l = 1;
	int bit = 0;
	while ( l <= len + m ) {
		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 ff[N] , gg[N] , ffr[N] , ggr[N] , invg[N] , q[N] , r[N] , tem[N];
int main(){
    scanf("%d%d",&n,&m);
    for ( int i = 0 ; i <= n ; ++i ) {
    	scanf("%lld",&ff[i]);
	}
	for ( int i = 0 ; i <= n ; ++i ) {
    	ffr[i] = ff[n - i];
	}
	for ( int i = 0 ; i <= m ; ++i ) {
		scanf("%lld",&gg[i]);
	}
	for ( int i = 0 ; i <= m ; ++i ) {
		ggr[i] = gg[m - i];
	}	
	for ( int i = n - m + 2 ; i <= m ; ++i ) {
		ggr[i] = 0;
		ffr[i] = 0;
	}
	int bit = 0;
	int len = 1;
	while ( len <= 2 * ( n - m + 1 ) ) {
		len <<= 1;
		++bit;
	}
	getinv( len , ggr , invg );
	for ( int i = n - m + 1 ; i < N ; ++i ) {
		invg[i] = 0;//取模,把后面的清零,不然会WA 
	}
	for ( int i = n - m + 1 ; i < N ; ++i ) {
		ffr[i] = 0;//取模,把后面的清零,不然会WA 
	}
	mul( ffr , invg , bit );
	for ( int i = 0 ; i <= n - m ; ++i ) {
		q[n - m - i] = ffr[i];
	}
	for ( int i = 0 ; i <= n - m ; ++i ) {
		printf("%lld ",q[i]);
	}
	puts("");
	bit = 0;
	len = 1;
	while ( len <= n + 2 ) { 
		len <<= 1;
		++bit;
	}
	mul( gg , q , bit );
	for ( int i = 0 ; i <= m - 1 ; ++i ) {
		r[i] = ( ( ff[i] - gg[i] ) % mod + mod ) % mod;
		printf("%lld ",r[i]);
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值