补补补……

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

泰勒展开

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

有公式

$$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. 设计WEB数据库(学习)

    设计WEB数据库 1.考虑建模的实际对象 为现实世界的实体和关系建立模型 在上面情况下考虑建表呢? 答:如果有一组属于同一类型的数据,就可以根据这些数据创建表 2.避免保存冗余数据 原因:a.空间的浪 ...

  2. cowboy的get和post的例子

    官方get和post的代码是有问题的,1.1下运行crash,这里修改了下,贴代码 创建工程 rebar-creator create-app testCowboy testCowboy_app.er ...

  3. struts2学习(15)struts2防重复提交

    一.重复提交的例子: 模拟一种情况,存在延时啊,系统比较繁忙啊啥的. 模拟延迟5s钟,用户点了一次提交,又点了一次提交,例子中模拟这种情况: 这样会造成重复提交:   com.cy.action.St ...

  4. Java经典练习题_Day05

    一. 选择题 1.下列各项中的各项定义正确的是:(ACD) A.  public static void m(){}   B.  public void static m(){} C.  public ...

  5. 汇编_指令_CS与DS的区别

    cs是值cpu执行的当前指令的段地址,ds是数据开始的段地址. CS是告诉CPU,去哪个位置找内容当成指令去执行:DS是告诉CPU,去哪个位置找内容当成数据被使用. datastring =ds co ...

  6. 北京师范大学第十六届程序设计竞赛决赛 F 汤圆防漏理论

    链接:https://www.nowcoder.com/acm/contest/117/F来源:牛客网 汤圆防漏理论 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他 ...

  7. 查询mysql数据库启动时间抛异常

    mysql 5.7.10使用dbforget Studio 连接异常 提示:The'INFORMATION_SCHEMA.SESSION_VARIABLES' feature is dis 查看mys ...

  8. hsqldb简单使用总结

      hsqldb数据库是一款纯Java实现的开源免费数据库,相对其他数据库来说,体积非常小,使用方便,非常利于在测试环境中使用,无需复杂的数据库配置.   hsqldb数据库引擎有几种服务器模式:Se ...

  9. java 检测代理IP是否准确

    我这里提供2个方法都可以实现:第一个是createIPAddress()和convertStreamToString() import java.io.IOException; import java ...

  10. 用php命令执行php脚本报错,在浏览器里执行却正常。

    写了一个Php脚本,里面用到了PDO连接数据库,但是所有的库都已经安装,在浏览器里执行完全正常,但是写到批处理文件里用php命令去执行的时候却报错找不到驱动,很奇怪. 经查找得知原来php命令与浏览器 ...