zoukankan      html  css  js  c++  java
  • 多项式全家桶

    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\_ \]

  • 相关阅读:
    C# 从服务器下载文件
    不能使用联机NuGet 程序包
    NPOI之Excel——合并单元格、设置样式、输入公式
    jquery hover事件中 fadeIn和fadeOut 效果不能及时停止
    UVA 10519 !! Really Strange !!
    UVA 10359 Tiling
    UVA 10940 Throwing cards away II
    UVA 10079 Pizze Cutting
    UVA 763 Fibinary Numbers
    UVA 10229 Modular Fibonacci
  • 原文地址:https://www.cnblogs.com/xixike/p/15614455.html
Copyright © 2011-2022 走看看