多项式求逆

本文介绍了一种高效计算多项式的逆元的方法,该方法利用快速傅立叶变换(FFT)来实现多项式求逆,特别适用于计算形式幂级数的逆。通过递归方式逐步增加精度,最终达到所需的项数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于ff,若有g,使得f×g(x)=1f×g(x)=1,称ggf的逆

ff,求g的前nn项,即求1f(x)的麦克劳林级数的前nn项系数

例:f(x)=1x

g(x)=11x=i=0xig(x)=11−x=∑i=0∞xi

f(x)f(x)存在逆常数项非0(否则就要出现c0c0的情况)

考虑倍增,设gtgt的前2t2t位与gg相同,其余位为0,显然有(f×g)(x)0(mod2t+1)(f×g)(x)≡0(mod2t+1)

显然g(t+1)(x)gt(x)g(t+1)(x)−gt(x)的前2t2t项为00,则(gt+1(x)gt(x))2的前2t+12t+1项为00,可得

(gt+122gt+1gt+gt2)(x)0(mod2t+1)

左乘f(x)f(x),由(f×gt+1)(x)1(mod2t+1)(f×gt+1)(x)≡1(mod2t+1)

得到(gt+12gt+f×g2t)(x)0(mod2t+1)(gt+1−2gt+f×gt2)(x)≡0(mod2t+1)

gt+1=2gtf×g2tgt+1=2gt−f×gt2

g0=a10g0=a0−1

T(n)=T(n2)+O(nlogn=O(nlog2n)T(n)=T(n2)+O(nlog⁡n=O(nlog2⁡n)

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353 , G = 3 , N = 270000;
int rev[N];
int a[N] , b[N] , c[N];
int read() {
    int ans = 0 , flag = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0') { ch = getchar();if(ch=='-') flag = -1;}
    while(ch <= '9' && ch >= '0') {ans = ans * 10 + ch - '0'; ch = getchar();}
    return ans * flag;
}
int qpow(int a , int b) {
    int ans = 1;
    while(b) {
        if(b & 1) ans = 1ll * ans * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    }
    return ans;
}
void dft(int *now , int n , int f) {
    for(int i = 0 ; i < n ; ++ i) if(i < rev[i]) swap(now[i] , now[rev[i]]);
    for(int i = 1 ; i < n ; i <<= 1) {
        int gn = qpow(G , (mod - 1) / (i<<1));//\frac{P-1}{n}
        if(f != 1) gn = qpow(gn , mod - 2);
        for(int j = 0 ; j < n ; j += (i<<1)) {
            int x , y , g = 1;
            for(int k = 0 ; k < i ; ++ k , g = 1ll * g * gn % mod) {
                x = now[j + k];
                y = 1ll * g * now[i + j + k] % mod;
                now[j + k] = (x + y) % mod;
                now[i + j + k] = ((x - y) % mod + mod ) % mod;
            }
        }
    }
    if(f != 1) {
        int nn = qpow(n , mod - 2);
        for(int i = 0 ; i < n ; ++ i)
            now[i] = 1ll * now[i] * nn % mod;
    }
}
void work(int deg , int *a , int *b) {
    if(deg == 1) {b[0] = qpow(a[0] , mod - 2); return;}
    work((deg + 1) >> 1 , a , b);
    int l = 0 , nn  , n = deg * 2;
    for(nn = 1 ; nn < n ; nn <<= 1) ++ l;
    for(int i = 0 ; i < nn ; ++ i)
        rev[i] = (rev[i>>1]>>1) | ((i & 1) << (l - 1));
    for(int i = 0 ; i < deg ; ++ i) c[i] = a[i];
    for(int i = deg ; i < nn ; ++ i) c[i] = 0;
    dft(c , nn , 1); dft(b , nn , 1);
    for(int i = 0 ; i < nn ; ++ i) b[i] = 1ll * (2 - 1ll * c[i] * b[i] % mod + mod ) % mod * b[i] % mod;
    dft(b , nn , -1);
    for(int i = deg ; i < nn ; ++ i) b[i] = 0;
}
int main() {
    int n = read();
    for(int i = 0 ; i < n ; ++ i) a[i] = read();
    work(n , a , b);
    for(int i = 0 ; i < n ; ++ i) printf("%d ", b[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值