「模板」多项式全家桶

#include <bits/stdc++.h>
#define ll long long
#define db double
#define gc getchar
#define pc putchar
#define swap(a, b) a ^= b ^= a ^= b

using namespace std;

const int N = 1e5 + 5;
const int p = 998244353;

namespace IO
{
    template <typename T>
    void read(T &x)
    {
        x = 0; bool f = 0; char c = gc();
        while(!isdigit(c)) f |= c == '-', c = gc();
        while(isdigit(c)) x = (ll)x * 10 % p + c - '0', c = gc();
        if(f) x = -x;
    }

    template <typename T>
    void write(T x)
    {
        if(x < 0) pc('-'), x = -x;
        if(x > 9) write(x / 10);
        pc('0' + x % 10);
    }
}
using namespace IO;

ll add(ll x) {return x < p ? x : x - p;}
ll sub(ll x) {return x < 0 ? x + p : x;}

ll qpow(ll a, int b)
{
    ll res = 1;
    while(b)
    {
        if(b & 1) res = res * a % p;
        a = a * a % p, b >>= 1;
    }
    return res;
}

namespace Poly
{
    const int G = 3;
    const int Gi = 332748118;
    const int inv2 = 499122177;

    void Clear(ll *a, int l, int r) {for(int i = l; i < r; i++) a[i] = 0;}
    void Copy(ll *a, ll *b, int len) {for(int i = 0; i < len; i++) b[i] = a[i];}
    void print(ll *a, int len) {for(int i = 0; i < len; i++) write(a[i]), pc(' '); pc('\n');}

    int rev[N << 2];

    void calcrev(int lim)
    {
        for(int i = 0; i < lim; i++)
            rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (lim >> 1));
    }

    int calclim(int len)
    {
        int lim = 1;
        while(lim < len) lim <<= 1;
        return lim;
    }

    //快速数论变换
    void NTT(ll *a, int lim, int type)
    {
        for(int i = 0; i < lim; i++)
            if(i < rev[i]) swap(a[i], a[rev[i]]);

        for(int mid = 1; mid < lim; mid <<= 1)
        {
            ll wn = qpow(type == 1 ? G : Gi, (p - 1) / (mid << 1));
            for(int i = 0; i < lim; i += (mid << 1))
            {
                ll w = 1;
                for(int j = 0; j < mid; j++, w = w * wn % p)
                {
                    ll x = a[i + j], y = w * a[i + mid + j] % p;
                    a[i + j] = add(x + y);
                    a[i + mid + j] = sub(x - y);
                }
            }
        }

        ll limi = qpow(lim, p - 2);
        if(type == -1)
            for(int i = 0; i < lim; i++)
                a[i] = a[i] * limi % p;
        return;
    }

    //多项式乘法
    ll A[N << 2], B[N << 2];

    void Mul(ll *a, ll *b, int n, int m)
    {
        int lim = calclim(n + m);
        Clear(A, 0, lim), Clear(B, 0, lim);
        Copy(a, A, n), Copy(b, B, m);
        calcrev(lim);
        NTT(A, lim, 1), NTT(B, lim, 1);
        for(int i = 0; i < lim; i++) A[i] = A[i] * B[i] % p;
        NTT(A, lim, -1);
        Copy(A, a, lim);
        return;
    }

    //多项式求逆
    ll c[N << 2];

    void Inv(ll *a, ll *b, int n)
    {
        if(n == 1) return b[0] = qpow(a[0], p - 2), void();
        Inv(a, b, (n + 1) >> 1);
        int lim = calclim(n << 1);
        Copy(a, c, n), Clear(c, n, lim);
        calcrev(lim);
        NTT(b, lim, 1), NTT(c, lim, 1);
        for(int i = 0; i < lim; i++)
            b[i] = sub(2ll - b[i] * c[i] % p) * b[i] % p;
        NTT(b, lim, -1);
        Clear(b, n, lim);
        return;
    }

    //多项式开根
    ll bi[N << 2];

    void Sqrt(ll *a, ll *b, int n)
    {
        if(n == 1) return b[0] = 1, void();
        Sqrt(a, b, (n + 1) >> 1);
        Clear(bi, 0, calclim(n << 1));
        Inv(b, bi, n);
        Mul(bi, a, n, n);
        for(int i = 0; i < n; i++) b[i] = add(bi[i] + b[i]) * inv2 % p;
        Clear(b, n, calclim(n << 1));
    }

    //多项式除法
    ll br[N << 2], bri[N << 2];

    void Div(ll *a, ll *b, ll *x, ll *y, int n, int m)
    {
        int lim = calclim(n << 1);
        Copy(b, br, m), Clear(br, m, lim), reverse(br, br + m);
        Clear(bri, 0, lim), Inv(br, bri, n - m + 2);
        Copy(a, x, n), Clear(x, n, lim), reverse(x, x + n);
        Mul(x, bri, n, n - m + 2), Clear(x, n - m + 1, n + n - m + 2), reverse(x, x + n - m + 1);
        reverse(br, br + m);
        Mul(br, x, m, n - m + 2);
        for(int i = 0; i < m - 1; i++) y[i] = sub(a[i] - br[i]);
        return;
    }

    //求导
    void Dif(ll *a, ll *b, int n)
    {
        for(int i = 1; i < n; i++)
            b[i - 1] = i * a[i] % p;
        b[n - 1] = 0;
    }

    //积分
    void Int(ll *a, ll *b, int n)
    {
        for(int i = 1; i < n; i++)
            b[i] = a[i - 1] * qpow(i, p - 2) % p;
        b[0] = 0;
    }

    //多项式ln
    ll x[N << 2], y[N << 2];

    void Ln(ll *a, ll *b, int n)
    {
        int lim = calclim(n << 1);
        Clear(x, 0, lim), Clear(y, 0, lim);
        Dif(a, x, n);
        Inv(a, y, n);
        Mul(x, y, n, n);
        Int(x, b, n);
        return;
    }

    //多项式exp
    ll lnb[N << 2];

    void Exp(ll *a, ll *b, int n)
    {
        if(n == 1) return b[0] = 1, void();
        Exp(a, b, (n + 1) >> 1);
        int lim = calclim(n << 1);
        Ln(b, lnb, n);
        for(int i = 0; i < n; i++)
            lnb[i] = sub(a[i] - lnb[i]);
        lnb[0]++;
        Mul(b, lnb, n, n), Clear(b, n, lim);
        return;
    }

    //多项式快速幂
    ll lna[N << 2];

    void Qpow(ll *a, int k, ll *b, int n)
    {
        Ln(a, lna, n);
        for(int i = 0; i < n; i++) lna[i] = lna[i] * k % p;
        Exp(lna, b, n);
        return;
    }
}
using namespace Poly;

int main()
{

}
// A.S.

未完待续。。。

posted @ 2021-11-28 21:06  Acestar  阅读(122)  评论(0编辑  收藏  举报