Luogu 4725 模板多项式对数函数

Posted czxingchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu 4725 模板多项式对数函数相关的知识,希望对你有一定的参考价值。

继续补全模板。

要求

$$g(x) = ln f(x)$$

两边求导,

$$g‘(x) = frac{f‘(x)}{f(x)}$$

然后左转去把多项式求导和多项式求逆的模板复制过来,就可以计算出$g‘(x)$,接下来再对$g‘(x)$求不定积分即可。

虽然我也不是很会不定积分,但是这就是求导的逆过程,相当于把求完导之后的函数搞回去。

因为$(a_ix^i)‘ = ia_ix^{i - 1}$,所以反向算一下就好。

求导的时间复杂度是$O(n)$,积分的时间复杂度是$O(nlogn)$,总时间复杂度是$O(nlogn)$。

Code:

技术分享图片
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 1 << 18;

int n;
ll f[N], g[N];

namespace Poly {
    const int L = 1 << 18;
    const ll P = 998244353LL;

    int lim, pos[L];
    ll f[L], g[L], h[L];
    
    inline ll fpow(ll x, ll y) {
        ll res = 1;
        for (x %= P; y > 0; y >>= 1) {
            if (y & 1) res = res * x % P;
            x = x * x % P;
        }
        return res;
    }
    
    inline void prework(int len) {
        int l = 0;
        for (lim = 1; lim < len; lim <<= 1, ++l);
        for (int i = 0; i < lim; i++)
            pos[i] = (pos[i >> 1] >> 1) | ((i & 1) << (l - 1));
    }
    
    inline void ntt(ll *c, int opt) {
        for (int i = 0; i < lim; i++)
            if (i < pos[i]) swap(c[i], c[pos[i]]);
        for (int i = 1; i < lim; i <<= 1) {
            ll wn = fpow(3, (P - 1) / (i << 1));
            if (opt == -1) wn = fpow(wn, P - 2);
            for (int len = i << 1, j = 0; j < lim; j += len) {
                ll w = 1;
                for (int k = 0; k < i; k++, w = w * wn % P) {
                    ll x = c[j + k], y = w * c[j + k + i] % P;
                    c[j + k] = (x + y) % P, c[j + k + i] = (x - y + P) % P;
                }
            }
        }
        
        if (opt == -1) {
            ll inv = fpow(lim, P - 2);
            for (int i = 0; i < lim; i++) c[i] = c[i] * inv % P;
        }
    }
    
    void inv(ll *a, ll *b, int len) {
        if (len == 1) {
            b[0] = fpow(a[0], P - 2);
            return;
        }
        
        inv(a, b, (len + 1) >> 1);
        prework(len << 1);
        for (int i = 0; i < lim; i++) f[i] = g[i] = 0;
        for (int i = 0; i < len; i++) f[i] = a[i], g[i] = b[i];
        ntt(f, 1), ntt(g, 1);
        for (int i = 0; i < lim; i++)
            g[i] = g[i] * (2LL - g[i] * f[i] % P + P) % P;
        ntt(g, -1);
        
        for (int i = 0; i < len; i++) b[i] = g[i];
    }
    
    inline void direv(ll *c, int len) {
        for (int i = 0; i < len - 1; i++) c[i] = c[i + 1] * (i + 1) % P;
        c[len - 1] = 0;
    }
    
    inline void integ(ll *c, int len) {
        for (int i = len - 1; i > 0; i--) c[i] = c[i - 1] * fpow(i, P - 2) % P;
        c[0] = 0;
    }
    
    inline void ln(ll *a, ll *b, int len) {
        for (int i = 0; i < len; i++) h[i] = 0;
        inv(a, h, len);
        
/*        for (int i = 0; i < len; i++)
            printf("%lld%c", h[i], " 
"[i == n - 1]);    */
        
        for (int i = 0; i < len; i++) b[i] = a[i];
        direv(b, len);
        
/*        for (int i = 0; i < len; i++)
            printf("%lld%c", b[i], " 
"[i == n - 1]);    */

        prework(len << 1);
        ntt(h, 1), ntt(b, 1);
        for (int i = 0; i < lim; i++) b[i] = b[i] * h[i] % P;
        ntt(b, -1);
        
/*        for (int i = 0; i < len; i++)
            printf("%lld%c", b[i], " 
"[i == n - 1]);    */

        integ(b, len);
    }
    
};

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for (; ch > 9|| ch < 0; ch = getchar())
        if (ch == -) op = -1;
    for (; ch >= 0 && ch <= 9; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

int main() {
    read(n);
    for (int i = 0; i < n; i++) read(f[i]);
    Poly :: ln(f, g, n);
    for (int i = 0; i < n; i++)
        printf("%lld%c", g[i], " 
"[i == n - 1]);
    return 0;
}
View Code

 

以上是关于Luogu 4725 模板多项式对数函数的主要内容,如果未能解决你的问题,请参考以下文章

luogu P4725 多项式对数函数 (模板题FFT多项式求逆求导和积分)

P4725 模板多项式对数函数

luogu P4726 多项式指数函数(模板题FFT多项式求逆多项式对数函数)

luogu5282 模板快速阶乘算法

HZOI2015帕秋莉的超级多项式

luogu4238 模板多项式求逆