• 多项式全家桶


    emm……NTT左转去看各位神犇的博客吧QwQ

    这里只贴代码及部分操作的推导过程。

    首先是喜闻乐见的 NTT 多项式乘法板子(这个就不解释了)

    namespace NTT{
        ll lim, len;
    
        inline ll qpow(ll a, ll b){
            ll res = 1;
            while(b){
                if(b & 1) res = res * a % mod;
                a = a * a % mod, b >>= 1;
            }
            return res;
        }
    
        inline void get_rev(cl n){
            lim = 1, len = 0;
            while(lim < n) lim <<= 1, ++len;
            for(int i = 0; i < lim; ++i) p[i] = (p[i >> 1] >> 1) | ((i & 1) << (len - 1));
        }
    
        inline void ntt(ll A[], cl lim, cl type){
            for(int i = 0; i < lim; ++i)
                if(i < p[i]) swap(A[i], A[p[i]]);
            for(int mid = 1; mid < lim; mid <<= 1){
                ll Wn = qpow(type == 1 ? G : Gi, (mod - 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 % mod){
                        ll x = A[i + j], y = w * A[i + j + mid] % mod;
                        A[i + j] = (x + y) % mod;
                        A[i + j + mid] = (x - y + mod) % mod;
                    }
                }
            }
            if(type == 1) return;
            ll inv = qpow(lim, mod - 2);
            for(int i = 0; i < lim; ++i) A[i] = A[i] * inv % mod;
        }
    
        inline void Mul(cl n, cl m, ll a[], ll b[]){
            get_rev(n + m);
            ntt(a, lim, 1), ntt(b, lim, 1);
            for(int i = 0; i < lim; ++i) a[i] = a[i] * b[i] % mod;
            ntt(a, lim, -1);
        }
    }
    using namespace NTT;
    

    紧跟着的是求逆

    已知多项式 \(F(x)\),要求 \(G(x)F(x) \equiv 1\ \ (mod\ \ x ^ n)\)

    \(H(x)F(x) \equiv 1 \ \ (mod\ x^{⌈ \frac{n}{2} ⌉})\)

    那么 $$(G(x) - H(x)) \equiv 0\ \ (mod\ \ x ^ {⌈ \frac{n}{2} ⌉})$$

    两边同时平方:

    \[(G(x) - H(x))^2 \equiv 0\ \ (mod\ \ x ^ {⌈ \frac{n}{2} ⌉}) \]

    \[G(x)^2 - 2G(x)H(x) + H(x)^2 \equiv 0\ \ (mod\ \ x ^ n) \]

    两边再同时乘上 \(F(x)\),由于 \(G(x)F(x) \equiv 1\ \ (mod\ \ x ^ n)\)

    \[G(x) - 2H(x) + H(x)^2F(x) \equiv 0\ \ (mod\ \ x ^ n) \]

    移一下项并提取公因多项式(:

    \[G(x) = H(x)(2 - H(x)F(x)) \]

    inline void Inv(ll n, ll a[], ll b[]){
        if(n == 1) return b[0] = qpow(a[0], mod - 2), void();
        Inv((n + 1) >> 1, a, b);
        get_rev(n << 1);
        for(int i = 0; i < n; ++i) c[i] = a[i];
        for(int i = n; i < lim; ++i) c[i] = 0;
        ntt(c, lim, 1), ntt(b, lim, 1);
        for(int i = 0; i < lim; ++i)
            b[i] = (2ll - c[i] * b[i] % mod + mod) * b[i] % mod;
        ntt(b, lim, -1);
        for(int i = n; i < lim; ++i) b[i] = 0;
    }
    

    然后是多项式开根

    我们已知 \(F(x)\),要求 \(G(x)\) 使得 \(G(x)^2 \equiv F(x)\ \ (mod\ \ x^n)\)

    \(H(x)^2 \equiv F(x)\ \ (mod\ x^{⌈ \frac{n}{2} ⌉})\)

    那么

    \[G(x) \equiv H(x)\ \ (mod\ x^{⌈ \frac{n}{2} ⌉}) \]

    \[(G(x) - H(x)) \equiv 0\ \ (mod\ x^{⌈ \frac{n}{2} ⌉}) \]

    还是两边同时平方:

    \[(G(x) - H(x))^2 \equiv 0\ \ (mod\ x^n) \]

    \[G(x)^2 - 2G(x)H(x) + H(x)^2 \equiv 0\ \ (mod\ \ x ^ n) \]

    由于 \(G(x)^2 \equiv F(x)\ \ (mod\ \ x^n)\) 得:

    \[F(x) - 2G(x)H(x) + H(x)^2 \equiv 0 \ \ (mod\ \ x ^ n) \]

    移项:

    \[G(x) = \frac{F(x) + H(x)^2}{2H(x)} \]

    那么事实上我的代码里的式子是这样的:

    \[G(x) = \frac{F(x)H(x)^{-1} + H(x)}{2} \]

    \(b(x) \rightarrow H(x)\)

    \(d(x) \rightarrow F(x)\)

    \(e(x) \rightarrow H(x)^{-1}\)

    inline void Sqrt(ll n, ll a[], ll b[]){
        if(n == 1) return b[0] = 1, void();
        Sqrt((n + 1) >> 1, a, b);
        get_rev(n << 1);
        memset(e, 0, sizeof(e));
        Inv(n, b, e);
        for(int i = 0; i < n; ++i) d[i] = a[i];
        for(int i = n; i < lim; ++i) d[i] = 0;
        ntt(d, lim, 1), ntt(e, lim, 1), ntt(b, lim, 1);
        for(int i = 0; i < lim; ++i) b[i] = (b[i] + d[i] * e[i] % mod) * inv2 % mod;
        ntt(b, lim, -1);
        for(int i = n; i < lim; ++i) b[i] = 0;
    }
    

    接着自然是是求 ln

    这个就需要通道一点微积分的知识了,虽然我也不太会QwQ,但是我会贺代码(。・ω・。)

    还是先来推推式子吧。

    已知 \(F(x)\),求 \(G(x) = lnF(x)\)

    众所周知\(g(x) = ln\,f(x)\) 的导数为 \(g'(x) = \frac{f'(x)}{f(x)}\),所以:

    \[G'(x) = \frac{F'(x)}{F(x)} \]

    也就是说,\(G'(x)\) 我们可以直接求出来了,下面给出不定积分的一个运算公式:

    \[\int x^a\,dx = \frac{1}{a + 1} x ^ {a + 1} \]

    由于我们的 \(G(x) = a_0 + a_1x + a_2x^2 + ···+ a_nx^n\)

    所以对每一项求个积分就完了,具体看代码吧。

    namespace Ln{
        inline void Diff(cl n, ll a[], ll b[]){//微分求导
            for(int i = 1; i < n; ++i) b[i - 1] = i * a[i];
            b[n - 1] = 0;
        }
    
        inline void Integral(cl n, ll a[], ll b[]){//积分
            for(int i = 1; i < n; ++i) b[i] = a[i - 1] * qpow(i, mod - 2) % mod;
            b[0] = 0;
        }
    
        inline void ln(cl n, ll a[], ll b[]){
            memset(f1, 0, sizeof(f1));
            memset(f2, 0, sizeof(f2));
            Diff(n, a, f1), Inv(n, a, f2);//f1(x) = a'(x),f2(x) = a(x)^(-1)
            Mul(n, n, f1, f2);//f1(x) = f1(x) * f2(x)
            Integral(n, f1, b);//g(x) = f(x) 的积分
        }
    }
    using namespace Ln;
    

    exp 当然也不能少

    已知 \(F(x)\),求 \(G(x) = e^{F(x)}\)

    emm……要用到泰勒展开,牛顿迭代什么的,然鹅我太蒻了,还不会QwQ。

    所以这里就只有结论了,像我这种蒟蒻还是全文背诵吧。

    \[F(x) = F_0(x) - \frac{G(F_0(x))}{G'(F_0(x)} \]

    再经过一番清新简单的推导过程之后……

    \[G(x) \equiv G_0(x)(1 - lnG_0(x) + F(x))\ \ (mod\ \ x ^ n) \]

    所以就可以算了……

    inline void Exp(cl n, ll a[], ll b[]){
        if(n == 1) return b[0] = 1, void();
        Exp((n + 1) >> 1, a, b);
        get_rev(n << 1);
        ln(n, b, d);// d(x) = ln(b(x))
        for(int i = 0; i < n; ++i) e[i] = a[i];
        for(int i = n; i < lim; ++i) e[i] = 0;
        ntt(d, lim, 1), ntt(e, lim, 1), ntt(b, lim, 1);
        for(int i = 0; i < lim; ++i) b[i] = (1ll - d[i] + e[i] + mod) * b[i] % mod;
        ntt(b, lim, -1);
        for(int i = n; i < lim; ++i) b[i] = 0;
    }
    

    最后是多项式除法

    除法由于其优秀的边界故不和上面放到一起,并且这里给出完整代码。

    以及我这里是计算出 \(Q(x)\)\(R(x)\) 后一块输出,由于计算 \(R(x)\) 时需要多项式卷积一下,因此对 \(Q(x)\) 做了一遍 \(NTT\),所以卷积完之后一定记得要 \(NTT\) 回来啊。

    来看看推导过程。

    已知 \(F(x)\)\(G(x)\),求 \(F(x) = Q(x)G(x) + R(x)\)

    首先就要用到一个神奇的操作,设 \(F_R(x)\)\(F(x)\) 把系数反过来之后的多项式,即

    \(F_R(x) = a_n + a_{n - 1}x + a_{n - 2}x^2 +···+a_0x^n\)

    容易发现,\(F_R(x) = x^nF(\frac{1}{x})\)

    然后就可以愉快的推式子啦 :)

    \[F(x) = Q(x)G(x) + R(x) \\ F(\frac{1}{x}) = Q(\frac{1}{x})G(\frac{1}{x}) + R(\frac{1}{x}) \]

    \(F(x)\)\(n\) 次的,\(G(x)\)\(m\) 次的,\(Q(x)\)\(n - m\) 次的,\(R(x)\)\(n - m - 1\) 次的,所以两边同乘 \(x^n\)

    \[x^nF(x) = x^{n - m}Q(x) * x^{m}G(x) + x^{n - m - 1}R(x) * x^{m + 1} \\ x^nF(x) \equiv x^{n - m}Q(x) * x^{m}G(x)\ \ (mod\ \ x^{n - m - 1}) \\ F_R(x) \equiv Q_R(x) * G_R(x)\ \ (mod\ \ x^{n - m - 1}) \\ Q_R(x) \equiv F_R(x) * G_R(x)^{-1}\ \ (mod\ \ x^{n - m - 1}) \]

    \(F_R(x)\)\(G_R(x)\) 都是已知的(预处理一下即可),\(Q(x)\) 就是 \(Q_R(x)\) 的系数倒过来。

    计算出 \(Q_(x)\) 之后 \(R(x)\) 也就简单了:

    \[R(x) = F(x) - Q(x)G(x) \]

    一定要注意边界啊啊啊啊!

    #include <bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    
    namespace IO{
        inline ll read(){
            ll x = 0;
            char ch = getchar();
            while(!isdigit(ch)) ch = getchar();
            while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
            return x;
        }
    
        template <typename T> inline void write(T x){
            if(x > 9) write(x / 10);
            putchar(x % 10 + '0');
        }
    
        inline void print(ll a[], ll n){
            for(int i = 0; i <= n; ++i) write(a[i]), putchar(' ');
            puts("");
        }
    }
    using namespace IO;
    
    const ll N = 3e5 + 10;
    const ll mod = 998244353;
    const ll G = 3, Gi = 332748118;
    ll n, m;
    ll c[N];
    ll f[N], g[N], fr[N], gr[N], ig[N];
    ll q[N], r[N], p[N];
    
    namespace NTT{
        ll lim, len;
    
        inline ll qpow(ll a, ll b){
            ll res = 1;
            while(b){
                if(b & 1) res = res * a % mod;
                a = a * a % mod, b >>= 1;
            }
            return res;
        }
    
        inline void get_rev(int n){
            lim = 1, len = 0;
            while(lim <= n) lim <<= 1, ++len;
            for(int i = 0; i <= lim; ++i) p[i] = (p[i >> 1] >> 1) | ((i & 1) << (len - 1));
        }
    
        inline void ntt(ll A[], ll lim, int type){
            for(int i = 0; i <= lim; ++i)
                if(i < p[i]) swap(A[i], A[p[i]]);
            for(int mid = 1; mid < lim; mid <<= 1){
                ll Wn = qpow(type == 1 ? G : Gi, (mod - 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 % mod){
                        ll x = A[i + j], y = w * A[i + j + mid] % mod;
                        A[i + j] = (x + y) % mod;
                        A[i + j + mid] = (x - y + mod) % mod;
                    }
                }
            }
            if(type == 1) return;
            ll inv = qpow(lim, mod - 2);
            for(int i = 0; i <= lim; ++i) A[i] = A[i] * inv % mod;
        }
    
        inline void Mul(ll n, ll m, ll a[], ll b[]){
            get_rev(n + m);
            ntt(a, lim, 1), ntt(b, lim, 1);
            for(int i = 0; i <= lim; ++i) a[i] = a[i] * b[i] % mod;
            ntt(a, lim, -1), ntt(b, lim, -1);//--------------------------NTT回来,NTT回来,NTT回来!!!
        }
    
        inline void Inv(ll n, ll a[], ll b[]){
            if(n == 0) return b[0] = qpow(a[0], mod - 2), void();
            Inv(n >> 1, a, b);
            get_rev(n << 1);
            for(int i = 0; i <= n; ++i) c[i] = a[i];
            for(int i = n + 1; i <= lim; ++i) c[i] = 0;
            ntt(c, lim, 1), ntt(b, lim, 1);
            for(int i = 0; i <= lim; ++i) b[i] = (2ll - c[i] * b[i] % mod + mod) * b[i] % mod;
            ntt(b, lim, -1);
            for(int i = n + 1; i <= lim; ++i) b[i] = 0;
        }
    }
    using namespace NTT;
    
    inline void Division(){
        for(int i = n - m + 1; i <= m; ++i) gr[i] = 0;
        Inv(n - m, gr, ig);
        Mul(n, n - m, fr, ig);
        for(int i = 0; i <= n - m; ++i) q[i] = fr[n - m - i];
        Mul(n - m, m, g, q);
        for(int i = 0; i < m; ++i) r[i] = (f[i] - g[i] + mod) % mod;
        print(q, n - m);
        print(r, m - 1);
    }
    
    signed main(){
        n = read(), m = read();
        for(int i = 0; i <= n; ++i) f[i] = read(), fr[n - i] = f[i];
        for(int i = 0; i <= m; ++i) g[i] = read(), gr[m - i] = g[i];
        Division();
        return 0;
    }
    

    最后再来一发 P5273 【模板】多项式幂函数 (加强版) 的代码,怒调 3h(

    #include <bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    
    const int mod = 998244353;
    const int G = 3, Gi = 332748118;
    const int N = 3e5 + 10;
    ll n, k, flag, phik;
    
    namespace IO{
        inline ll read(){
            ll x = 0;
            char ch = getchar();
            while(!isdigit(ch)) ch = getchar();
            while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
            return x;
        }
    
        inline ll readk(){
            ll k = 0;
            char ch = getchar();
            while(!isdigit(ch)) ch = getchar();
            while(isdigit(ch)){
                if((k << 3) + (k << 1) + ch - '0' > n) flag = 1;
                k = ((k << 3) + (k << 1) + ch - '0') % mod;
                phik = ((phik << 3) + (phik << 1) + ch - '0') % (mod - 1);
                ch = getchar();
            }
            return k;
        }
    
        template <typename T> inline void write(T x){
            if(x > 9) write(x / 10);
            putchar(x % 10 + '0');
        }
    }
    using namespace IO;
    
    ll A[N], B[N], c[N], lnb[N];
    ll lowa[N], lna[N];
    int p[N];
    ll f1[N], f2[N], f[N], g[N];
    
    inline ll qpow(ll a, int b){
        ll res = 1;
        while(b){
            if(b & 1) res = res * a % mod;
            a = a * a % mod, b >>= 1;
        }
        return res;
    }
    
        
    inline void clear(ll a[], int l, int r) {for(int i = l; i < r; ++i) a[i] = 0;}
    inline void clone(ll a[], ll b[], int n) {for(int i = 0; i < n; ++i) a[i] = b[i];}
    inline void print(ll a[], int n) {for(int i = 0; i < n; ++i) write(a[i]), putchar(' '); puts("");}
    
    namespace NTT{
        int lim, len;
    
        inline void get_rev(int n){
            lim = 1, len = 0;
            while(lim < n) lim <<= 1, ++len;
            for(int i = 0; i < lim; ++i) p[i] = (p[i >> 1] >> 1) | ((i & 1) << (len - 1));
        }
    
        inline void ntt(ll A[], int lim, int type){
            for(int i = 0; i < lim; ++i)
                if(i < p[i]) swap(A[i], A[p[i]]);
            for(int mid = 1; mid < lim; mid <<= 1){
                ll Wn = qpow(type == 1 ? G : Gi, (mod - 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 % mod){
                        ll x = A[i + j], y = w * A[i + j + mid] % mod;
                        A[i + j] = (x + y) % mod;
                        A[i + j + mid] = (x - y + mod) % mod;
                    }
                }
            }
            if(type == 1) return;
            ll inv = qpow(lim, mod - 2);
            for(int i = 0; i < lim; ++i) A[i] = A[i] * inv % mod;
        }
    
        inline void Mul(int n, int m, ll a[], ll b[]){
            get_rev(n + m);
            clear(A, 0, lim), clear(B, 0, lim);
            clone(A, a, n), clone(B, b, m);
            ntt(A, lim, 1), ntt(B, lim, 1);
            for(int i = 0; i < lim; ++i) A[i] = A[i] * B[i] % mod;
            ntt(A, lim, -1);
            clone(a, A, lim);
        }
    
        inline void Inv(int n, ll a[], ll b[]){
            if(n == 1) return b[0] = qpow(a[0], mod - 2), void();
            Inv((n + 1) >> 1, a, b);
            get_rev(n << 1);
            clone(c, a, n), clear(c, n, lim);
            ntt(b, lim, 1), ntt(c, lim, 1);
            for(int i = 0; i < lim; ++i) b[i] = (2ll - c[i] * b[i] % mod + mod) * b[i] % mod;
            ntt(b, lim, -1);
            clear(b, n, lim);
        }
    }
    using namespace NTT;
    
    namespace Poly{
        inline void Diff(int n, ll a[], ll b[]){
            for(int i = 1; i < n; ++i) b[i - 1] = i * a[i] % mod;
            b[n - 1] = 0;
        }
    
        inline void Integral(int n, ll a[], ll b[]){
            for(int i = 1; i < n; ++i) b[i] = a[i - 1] * qpow(i, mod - 2) % mod;
            b[0] = 0;
        }
    
        inline void Ln(int n, ll a[], ll b[]){
            get_rev(n << 1);
            clear(f1, 0, lim), clear(f2, 0, lim);
            Diff(n, a, f1), Inv(n, a, f2);
            Mul(n, n, f1, f2);
            Integral(n, f1, b);
        }
    
        inline void Exp(int n, ll a[], ll b[]){
            if(n == 1) return b[0] = 1, void();
            Exp((n + 1) >> 1, a, b);
            get_rev(n << 1);
            Ln(n, b, lnb);
            for(int i = 0; i < n; ++i) lnb[i] = (a[i] - lnb[i] + mod) % mod;
            lnb[0]++;
            Mul(n, n, b, lnb), clear(b, n, lim);
        }
    
        inline void Qpow(int n, int k, ll a[], ll b[], ll phik){
            clear(b, 0, n);
            int shift = 0;
            while(!a[shift]) shift++;
            if((ll)shift * k >= n) return;
            n -= shift;
            for(int i = 0; i < n; ++i) lowa[i] = a[i + shift];
            int low0 = lowa[0], inv0 = qpow(low0, mod - 2);
            for(int i = 0; i < n; ++i) lowa[i] = lowa[i] * inv0 % mod;
            Ln(n, lowa, lna);
            for(int i = 0; i < n; ++i) lna[i] = lna[i] * k % mod;
            Exp(n, lna, b);
            shift *= k;
            for(int i = n + shift - 1; i >= shift; --i) b[i] = b[i - shift] * qpow(low0, phik) % mod;
            clear(b, 0, shift);
        }
    }
    using namespace Poly;
    
    int main(){
        n = read(), k = readk();
        for(int i = 0; i < n; ++i) f[i] = read();
        if(!flag || f[0]) Qpow(n, k, f, g, phik);
        print(g, n);
        return 0;
    }
    

    \[\_EOF\_ \]

  • 相关阅读:
    DNS知识点
    云服务镜像模板配置
    CoreDNS域名解析问题
    ingress配置静态资源
    PAGE_SIZE THREAD_SIZE STACKSIZE
    dockerregistry
    内联视图无法发生视图合并的几种情况
    oracle列删除
    ash_top_plsql
    oracle json()函数put优化案例
  • 原文地址:https://www.cnblogs.com/xixike/p/15614455.html
Copyright © 2020-2023  润新知