补补补……

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

泰勒展开

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

有公式

$$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. nginx虚拟机的配置

    user nginx nginx;worker_processes 1;pid /data/var/run/nginx/nginx.pid;worker_rlimit_nofile 51200; ev ...

  2. RedHat6.5用ISO配置yum源

    CentOS自带强大的yum功能,默认为从网上自动下载rpm包,对于网速不太给力或者没有网络的情况下需要用的话就不是很方便,很多软件尤其是服务器上的软件我们么有必要追求最新,稳定性最重要,这里我们用C ...

  3. 如何使用indexdb

    一.实现步骤 1)获得indexedDB对象 if (!window.indexedDB) { window.indexedDB = window.mozIndexedDB || window.web ...

  4. POJ 2991 Crane(线段树)

    Crane Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7687   Accepted: 2075   Special J ...

  5. 第十章 hbase默认配置说明

    hbase.rootdir:这个目录是region  server的共享目录,用来持久化Hbase.URL需要是'完全正确'的,还要包含文件系统的scheme.例如,要表示hdfs中的 '/hbase ...

  6. 【学习笔记】FFT

    1.内容 由于noble_太懒 不想写了 非常好的博客: https://www.cnblogs.com/rvalue/p/7351400.html http://www.cnblogs.com/ca ...

  7. rails 网站跨域

    7down voteaccepted gem install rack-cors Or in your Gemfile: gem 'rack-cors', :require => 'rack/c ...

  8. JAVA_01

    Java局部变量 局部变量声明在方法.构造方法或者语句块中: 局部变量在方法.构造方法.或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁: 访问修饰符不能用于局部变量: 局部变量只在声明它 ...

  9. MyBatis 学习记录6 TypeHandler

    主题 因为对MyBatis在JDBC数据和Java对象之间数据转化比较感兴趣,所以就记录并学习一下TypeHandler. 使用场景 如上图所示,观察下接口方法就能明白.TypeHandler主要用于 ...

  10. mysql utf8方式连接查看表数据乱码的问题

    起因 今天在公司第一次链接一个新的mysql数据库,我看到在spring里配置的jdbc里datasource.url里有配置characterEncoding=utf8..然后就用navicat开选 ...