补补补……

这个题的解法让我认识到了泰勒展开的美妙之处。

泰勒展开

泰勒展开就是用一个多项式型的函数去逼近一个难以准确描述的函数。

有公式

$$f(x)\approx g(x) = g(x_0) + \frac{g'(x_0)}{1!}(x - x_0) + \frac{g^{(2)}(x_0)}{2!}(x - x_0)^2 + \cdots + \frac{g^{(n)}(x_0)}{n!}(x - x_0)^n$$

在这里$g^{(n)}$表示$g$的$n$阶导。

在$0$这个点的泰勒展开$(x_0 = 0)$叫做麦克劳林级数,利用这个东西可以很精确地去逼近原来的函数。

比如,

$$f(x) = e^x \approx 1 + \frac{x}{1!} + \frac{x^2}{2!} + \frac{x^3}{3!} + \frac{x^4}{4!} + \cdots = \sum_{i = 0}^{\infty}\frac{x^i}{i!}$$

$$f(x) = ln(1 - x) \approx 0 - \frac{x}{1} - \frac{x^2}{2} - \frac{x^3}{3} - \cdots = -\sum_{i = 1}^{\infty}\frac{x^i}{i}$$

一些定义

有了泰勒展开之后我们可以定义一些看上去很难定义的东西,比如这个多项式取$ln$:

$$ln(1 - A(x)) = -\sum_{i = 1}^{\infty}\frac{A(x)^i}{i}$$

这也解释了为什么给出的多项式常数项一定是$1$。

同理,多项式$exp$的定义是这样子的:

$$exp(A(x)) = \sum_{i = 0}^{\infty}\frac{A(x)^i}{i!}$$

可以发现这样子定义之后就好理解很多了。

牛顿迭代

牛顿迭代是解决多项式问题的利器,可以一口气解决好多推式子问题。

牛顿法求解方程的近似解在高中课本里面有提到过,它也可以用来解决多项式的问题。

现在我们尝试用另一种手段得到它。

要求$G(F(x)) \equiv 0 (\mod x^n)$,通过题目给出的特定的$G(x)$,我们可以算出$F(x)$。

首先我们尝试解决常数项的问题,一般来说,这个问题都非常简单,比如本题中可以直接求出常数项为$1$。

现在假设已经求出了$F_0(x)$满足$G(F_0(x)) \equiv 0(\mod x^{\left \lceil \frac{n}{2} \right \rceil})$,我们尝试求$F(x)$满足$G(F(x)) \equiv 0 (\mod x^n)$。

可以对$G(x)$在$F_0(x)$处进行泰勒展开,得到

$$G(F(x)) = G(F_0(x)) + G'(F_0(x))(F(x) - F_0(x)) + G''(F_0(x))(F(x) - F_0(x))^2 + \cdots$$

这是在$(\mod x^n)$次意义下进行的,在多项式求逆的时候已经证明过了$(F(x) - F_0(x))^n$在$n \geq 2$的时候模$x^n$为$0$,后面就可以不用写下去了。

注意到$G(F(x)) \equiv 0 (\mod x^n)$,有

$$F(x) \equiv F_0(x) - \frac{G(F_0(x))}{G'(F_0(x))}(\mod x^n)$$

这个式子就是牛顿迭代的式子了。

鼓掌~~~

考虑一下在这个题中怎么取这个$G(x)$。

题目要求

$$B(x) \equiv e^{A(x)} (\mod x^n)$$

发现这个$e^x$并不是很方便,所以两边取一下$ln$,移项,

$$ln(B(x)) - A(x) \equiv 0 (\mod x^n)$$

那就记$G(F(x)) = lnF(x) - A(x)$。

因为$A(x)$和$F(x)$在这里等价于两个数,$F(x)$是自变量,所以$A(x)$就看成常数,在求导的时候可以直接消掉。

整理一下就得到了多项式$exp$的式子:

$$F(x) = F_0(x)(1 - lnF_0(x) + A(x))$$

左转去复制各种模板过来,于是就可以愉快地递归求解了!

时间复杂度仍然是$O(nlogn)$。

在吉老师博客里看到了关于这堆东西常数的吐槽,感觉妙不可言。

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll; const int N = << ; int n;
ll f[N], g[N]; namespace Poly {
const int L = << ;
const ll P = 998244353LL; int lim, pos[L];
ll f[L], g[L], h[L], tmp[L]; inline ll fpow(ll x, ll y) {
ll res = ;
for (x %= P; y > ; y >>= ) {
if (y & ) res = res * x % P;
x = x * x % P;
}
return res;
} inline void prework(int len) {
int l = ;
for (lim = ; lim < len; lim <<= , ++l);
for (int i = ; i < lim; i++)
pos[i] = (pos[i >> ] >> ) | ((i & ) << (l - ));
} inline void ntt(ll *c, int opt) {
for (int i = ; i < lim; i++)
if (i < pos[i]) swap(c[i], c[pos[i]]);
for (int i = ; i < lim; i <<= ) {
ll wn = fpow(, (P - ) / (i << ));
if (opt == -) wn = fpow(wn, P - );
for (int len = i << , j = ; j < lim; j += len) {
ll w = ;
for (int k = ; 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 == -) {
ll inv = fpow(lim, P - );
for (int i = ; i < lim; i++) c[i] = c[i] * inv % P;
}
} void inv(ll *a, ll *b, int len) {
if (len == ) {
b[] = fpow(a[], P - );
return;
} inv(a, b, (len + ) >> );
prework(len << );
for (int i = ; i < lim; i++) f[i] = g[i] = ;
for (int i = ; i < len; i++) f[i] = a[i], g[i] = b[i];
ntt(f, ), ntt(g, );
for (int i = ; i < lim; i++)
g[i] = g[i] * (2LL - g[i] * f[i] % P + P) % P;
ntt(g, -); for (int i = ; i < len; i++) b[i] = g[i];
} inline void direv(ll *c, int len) {
for (int i = ; i < len - ; i++) c[i] = c[i + ] * (i + ) % P;
c[len - ] = ;
} inline void integ(ll *c, int len) {
for (int i = len - ; i > ; i--) c[i] = c[i - ] * fpow(i, P - ) % P;
c[] = ;
} inline void ln(ll *a, ll *b, int len) {
for (int i = ; i < len; i++) h[i] = ;
inv(a, h, len); for (int i = ; i < len; i++) b[i] = a[i];
direv(b, len); prework(len << );
ntt(h, ), ntt(b, );
for (int i = ; i < lim; i++) b[i] = b[i] * h[i] % P;
ntt(b, -); integ(b, len);
// for (int i = 0; i < lim; i++) h[i] = 0;
} ll F[L], G[L];
void exp(ll *a, ll *b, int len) {
if (len == ) {
b[] = ;
return;
}
exp(a, b, (len + ) >> ); ln(b, F, len);
F[] = (a[] % P + - F[] + P) % P;
for (int i = ; i< len; i++) F[i] = (a[i] - F[i] + P) % P; prework(len << );
for (int i = len; i < lim; i++) F[i] = ;
for (int i = ; i < lim; i++) G[i] = ;
for (int i = ; i < len; i++) G[i] = b[i];
ntt(F, ), ntt(G, );
for (int i = ; i < lim; i++) F[i] = F[i] * G[i] % P;
ntt(F, -); for (int i = ; i < lim; i++) b[i] = F[i];
} }; template <typename T>
inline void read(T &X) {
X = ; char ch = ; T op = ;
for (; ch > ''|| ch < ''; ch = getchar())
if (ch == '-') op = -;
for (; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} int main() {
read(n);
for (int i = ; i < n; i++) read(f[i]);
Poly :: exp(f, g, n);
for (int i = ; i < n; i++)
printf("%lld%c", g[i], " \n"[i == n - ]);
return ;
}

一点点推广

取不同的$G(x)$可以得到不同的结果,简单推导出答案的式子。

比如:

多项式求逆,

$$G(F(x)) = F(x)A(x) - 1$$

多项式开方,

$$G(F(x)) = F(x)^2 - A(x)$$

……

还有一些应该会在做题的时候补全。

Luogu 4726 【模板】多项式指数函数的更多相关文章

  1. [洛谷P4726]【模板】多项式指数函数

    题目大意:给出$n-1$次多项式$A(x)$,求一个 $\bmod{x^n}$下的多项式$B(x)$,满足$B(x) \equiv e^{A(x)}$. 题解:(by Weng_weijie) 泰勒展 ...

  2. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  3. Luogu P2742 模板-二维凸包

    Luogu P2742 模板-二维凸包 之前写的实在是太蠢了.于是重新写了一个. 用 \(Graham\) 算法求凸包. 注意两个向量 \(a\times b>0\) 的意义是 \(b\) 在 ...

  4. luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

    luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...

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

    手动博客搬家: 本文发表于20181127 08:39:42, 原地址https://blog.csdn.net/suncongbo/article/details/84559818 题目链接: ht ...

  6. luogu P4726 【模板】多项式指数函数 多项式 exp 牛顿迭代 泰勒展开

    LINK:多项式 exp 做多项式的题 简直在嗑药. 前置只是 泰勒展开 这个东西用于 对于一个函数f(x) 我们不好得到 其在x处的取值. 所以另外设一个函数g(x) 来在x点处无限逼近f(x). ...

  7. 洛谷P4726 【模板】多项式指数函数(多项式exp)

    题意 题目链接 Sol 多项式exp,直接套泰勒展开的公式 \(F(x) = e^{A(x)}\) 求个导\(F'(x) = A(x)\) 我们要求的就是\(G(f(x)) = lnF(x) - A( ...

  8. Luogu4726 【模板】多项式指数函数(NTT+多项式求逆)

    https://www.cnblogs.com/HocRiser/p/8207295.html 安利! #include<iostream> #include<cstdio> ...

  9. P4726 【模板】多项式指数函数

    思路 按照式子计算即可 \[ F(x)=F_0(x)(1-\ln F_0(x) +A(x)) \] 代码 // luogu-judger-enable-o2 #include <cstdio&g ...

随机推荐

  1. 搭建基于hyperledger fabric的联盟社区(九) --检索状态数据库

    一.启动elasticsearch服务 官网下载压缩包解压,进入bin目录启动: ./elasticsearch 通过ip访问 localhost:9200,可以看到如下信息 { name: &quo ...

  2. Memory stream is not expandable

    发现项目有一个地方在做图片缩放剪切的一个操作中.碰到有一些特殊的图片会报 Memory stream is not expandable 的错误 跟踪的时候发现是 由方法 originalStream ...

  3. Google Cloud IAM中添加自定义域名

    Google Cloud支持在IAM中加入自定义域名.具体方法如下: 1. 登录Google Cloud Console,点击IAM和管理,进入身份和组织 2. 点击注册 3. 会重定向到Gmail到 ...

  4. python 异常处理,约束

    异常处理: 在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是显示一个提示的页面. try: pass except Exception,ex: pass 例如: whi ...

  5. Java-Runoob:Java Character 类

    ylbtech-Java-Runoob:Java Character 类 1.返回顶部 1. Java Character 类 Character 类用于对单个字符进行操作. Character 类在 ...

  6. 关于使用PyExecJS+nodejs使用与js反混淆

    来源:https://cuiqingcai.com/5024.html 梳理这篇博客的时候出问题,我默认的是jscript作为pyexcJs的引擎,问题很大,大部分的js都无法加载,各种包用不了,只能 ...

  7. css移除a标签及map、area(图片热区映射)点击过后的边框

    默认a标签及其包含的html元素和map中的area(图片热区映射)在点击过后留有默认的蓝色边框,如下图 可以看到,蓝色的边框破坏了页面的整体美感,很多时候我们都是不需要的.通过设置相应的css可以去 ...

  8. JNI(java Native Interface)

    参看: http://blog.csdn.net/xw13106209/article/details/6989415

  9. Android 4 学习(17):使用Content Resolver

    Content Resolver简介 每个应用程序都有一个ContentResolver实例,通过getContentResolver()方法可以获取: ContentResolver cr = ge ...

  10. [转] C#中out和ref之间的区别

    gskcc 的原文地址 C#中out和ref之间的区别 首先:两者都是按地址传递的,使用后都将改变原来参数的数值. 其次:ref可以把参数的数值传递进函数,但是out是要把参数清空,就是说你无法把一个 ...