【知识总结】多项式全家桶(二)(ln和exp)
上一篇:【知识总结】多项式全家桶(一)(NTT、加减乘除和求逆)
一、对数函数\(\ln(A)\)
求一个多项式\(B(x)\),满足\(B(x)=\ln(A(x))\)。
这里需要一些最基本的微积分知识(不会?戳我(暂时戳不动):【知识总结】微积分初步挖坑待填)。
另外,\(n\)次多项式\(A(x)\)可以看成关于\(x\)的\(n\)次函数,可以对其求导。显然,\(A(x)=\sum\limits_{i=0}^{n-1}a_ix^i\)的导数是\(A'(x)=\sum\limits_{i=0}^{n-2}a_{i+1}x^i(i+1)\),积分是\(\int A(x)\mathrm{d} x=\sum\limits_{i=1}^{n}\frac{a_{i-1}}{i}x^i\)。可以写出如下代码(非常简单):
void derivative(const int *a, int *b, const int n)
{
for (int i = 1; i < n; i++)
b[i - 1] = (ll)a[i] * i % p;
b[n - 1] = 0;
}
void integral(const int *a, int *b, const int n)
{
for (int i = n - 1; i >= 0; i--)
b[i + 1] = (ll)a[i] * inv(i + 1) % p;
b[0] = 0;
}
\(f(x)=\ln(x)\)的导数是\(f'(x)=\frac{1}{x}\)。回到原问题,对两边同时求导,得到(要用一下链式法则\(g(f(x))\)的导数是\(g'(f(x))f'(x)\)):
\]
求个\(A(x)\)的逆元(多项式求逆)然后乘上\(A'(x)\),最后把\(B(x)\)积分回去就好了。
至于代码……下面算多项式指数函数的时候要算对数函数,所以暂时省略。
二、指数函数\(\exp(x)\)
求多项式\(B(x)\)满足\(B(x)=e^{A(x)}\)。
首先,这个式子相当于求\(\ln B(x)=A(x)\)即\(\ln B(x)-A(x)=0\)
设关于多项式的函数\(F(B(x))=\ln B(x)-A(x)\),那么问题就是求这个函数的零点(\(A(x)\)是给定的,视作常数)。
求函数零点的方法之一是牛顿迭代,公式如下(\(i\)是迭代次数,\(x\)是自变量,\(F(x)\)是要求零点的函数,\(F'(x_0)\)是\(F(x)\)在\(x_0\)处的导数):
\]
把\(F(B(x))=\ln B(x)-A(x)\)求导,得到\(F'(B(x))=\frac{1}{B(x)}\)(注意自变量是\(B(x)\)不是\(x\)。这不是一个\(F(x)\)和\(B(x)\)的复合函数)。然后代入上面的公式:
B_{i+1}(x)&=B_i(x)-\frac{\ln B_i(x)-A(x)}{\frac{1}{B_i(x)}}\\
&=B_i(x)-B_i(\ln B_i(x)-A(x))\\
&=B_i(x)(1-\ln B_i(x)-A(x))
\end{aligned}
\]
由于多项式乘法的存在,每迭代一次\(B\)的有效长度会增加一倍。
下一篇:【知识总结】多项式全家桶(三)(任意模数NTT)
代码(洛谷4726):
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#undef i
#undef j
#undef k
#undef true
#undef false
#undef min
#undef max
#undef swap
#undef sort
#undef if
#undef for
#undef while
#undef printf
#undef scanf
#undef putchar
#undef getchar
#define _ 0
using namespace std;
namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
typedef long long ll;
const int N = 1e5 + 10, LEN = (N << 2), p = 998244353, g = 3;
namespace Polynomial
{
inline int power(int a, int b)
{
int ans = 1;
while (b)
{
if (b & 1)
ans = (ll)ans * a % p;
a = (ll)a * a % p;
b >>= 1;
}
return ans;
}
inline int inv(const int a)
{
return power(a, p - 2);
}
int omega[LEN], winv[LEN], rev[LEN];
void init(const int n, const int lg2)
{
int w = power(g, (p - 1) / n), wi = inv(w);
omega[0] = winv[0] = 1;
for (int i = 1; i < n; i++)
{
omega[i] = (ll)omega[i - 1] * w % p;
winv[i] = (ll)winv[i - 1] * wi % p;
}
for (int i = 0; i < n; i++)
rev[i] = ((rev[i >> 1] >> 1) | ((i & 1) << (lg2 - 1)));
}
void ntt(int *a, const int *w, const int n)
{
for (int i = 0; i < n; i++)
if (i < rev[i])
swap(a[i], a[rev[i]]);
for (int l = 1; l < n; l <<= 1)
for (int i = 0; i < n; i += (l << 1))
for (int k = 0; k < l; k++)
{
int tmp = (a[i + k] - (ll)w[n / (l << 1) * k] * a[i + l + k] % p + p) % p;
a[i + k] = (a[i + k] + (ll)w[n / (l << 1) * k] * a[i + l + k] % p) % p;
a[i + l + k] = tmp;
}
}
void mul(const int *a, const int *b, int *c, const int n)
{
static int x[LEN], y[LEN];
int m = 1, lg2 = 0;
while (m < (n << 1) - 1)
m <<= 1, ++lg2;
init(m, lg2);
memcpy(x, a, sizeof(int[n]));
memset(x + n, 0, sizeof(int[m - n]));
memcpy(y, b, sizeof(int[n]));
memset(y + n, 0, sizeof(int[m - n]));
ntt(x, omega, m), ntt(y, omega, m);
for (int i = 0; i < m; i++)
x[i] = (ll)x[i] * y[i] % p;
ntt(x, winv, m);
int invm = inv(m);
for (int i = 0; i < m; i++)
x[i] = (ll)x[i] * invm % p;
memcpy(c, x, sizeof(int[n]));
}
void _inv(const int *a, int *b, const int n)
{
if (n == 1)
b[0] = inv(a[0]);
else
{
static int tmp[LEN];
_inv(a, b, (n + 1) >> 1);
int m = 1, lg2 = 0;
while (m < (n << 1) + 1)
m <<= 1, ++lg2;
init(m, lg2);
memcpy(tmp, a, sizeof(int[n]));
memset(tmp + n, 0, sizeof(int[m - n]));
memset(b + ((n + 1) >> 1), 0, sizeof(int[m - ((n + 1) >> 1)]));
ntt(tmp, omega, m);
ntt(b, omega, m);
for (int i = 0; i < m; i++)
b[i] = (b[i] * 2LL % p - (ll)tmp[i] * b[i] % p * b[i] % p + p) % p;
ntt(b, winv, m);
int invm = inv(m);
for (int i = 0; i < m; i++)
b[i] = (ll)b[i] * invm % p;
memset(b + n, 0, sizeof(int[m - n]));
}
}
void inv(const int *a, int *b, const int n)
{
static int tmp[LEN];
memcpy(tmp, a, sizeof(int[n]));
_inv(tmp, b, n);
}
void derivative(const int *a, int *b, const int n)
{
for (int i = 1; i < n; i++)
b[i - 1] = (ll)a[i] * i % p;
b[n - 1] = 0;
}
void integral(const int *a, int *b, const int n)
{
for (int i = n - 1; i >= 0; i--)
b[i + 1] = (ll)a[i] * inv(i + 1) % p;
b[0] = 0;
}
void ln(const int *a, int *b, const int n)
{
static int tmp[LEN], inva[LEN];
derivative(a, tmp, n);
inv(a, inva, n - 1);
mul(inva, tmp, b, n - 1);
integral(b, b, n - 1);
}
void _exp(const int *a, int *b, const int n)
{
if (n == 1)
b[0] = 1;
else
{
static int tmp[LEN];
_exp(a, b, (n + 1) >> 1);
ln(b, tmp, n);
for (int i = 0; i < n; i++)
tmp[i] = (-tmp[i] + a[i] + p) % p;
tmp[0] = (tmp[0] + 1) % p;
mul(b, tmp, b, n);
}
}
void exp(const int *a, int *b, const int n)
{
static int tmp[LEN];
memcpy(tmp, a, sizeof(int[n]));
_exp(tmp, b, n);
}
}
int work()
{
static int a[LEN];
int n;
read(n);
for (int i = 0; i < n; i++)
read(a[i]);
Polynomial::exp(a, a, n);
for (int i = 0; i < n; i++)
write(a[i]), putchar(' ');
return (0^_^0);
}
}
int main()
{
return zyt::work();
}
【知识总结】多项式全家桶(二)(ln和exp)的更多相关文章
- 【知识总结】多项式全家桶(三)(任意模数NTT)
经过两个月的咕咕,"多项式全家桶" 系列终于迎来了第三期--(雾) 上一篇:[知识总结]多项式全家桶(二)(ln和exp) 先膜拜(伏地膜)大恐龙的博客:任意模数 NTT (在页面 ...
- 【知识总结】多项式全家桶(一)(NTT、加减乘除和求逆)
我这种数学一窍不通的菜鸡终于开始学多项式全家桶了-- 必须要会的前置技能:FFT(不会?戳我:[知识总结]快速傅里叶变换(FFT)) 以下无特殊说明的情况下,多项式的长度指多项式最高次项的次数加\(1 ...
- [模板]多项式全家桶小记(求逆,开根,ln,exp)
前言 这里的全家桶目前只包括了\(ln,exp,sqrt\).还有一些类似于带余数模,快速幂之类用的比较少的有时间再更,\(NTT\)这种前置知识这里不多说. 还有一些基本的导数和微积分内容要了解,建 ...
- 用 Vue 全家桶二次开发 V2EX 社区
一.开发背景 为了全面的熟悉Vue+Vue-router+Vuex+axios技术栈,结合V2EX的开放API开发了这个简洁版的V2EX. 在线预览 (为了实现跨域,直接npm run dev部署的, ...
- loj#6363. 「地底蔷薇」(拉格朗日反演+多项式全家桶)
题面 传送门 题解 肝了一个下午--我老是忘了拉格朗日反演计算的时候多项式要除以一个\(x\)--结果看它推倒简直一脸懵逼-- 做这题首先你得知道拉格朗日反演是个什么东西->这里 请坐稳,接下来 ...
- Solution -「LOJ #150」挑战多项式 ||「模板」多项式全家桶
\(\mathcal{Description}\) Link. 给定 \(n\) 次多项式 \(F(x)\),在模 \(998244353\) 意义下求 \[G(x)\equiv\left\{ ...
- bzoj3684: 大朋友和多叉树(拉格朗日反演+多项式全家桶)
题面 传送门 题解 首先你得知道什么是拉格朗日反演->这里 我们列出树的个数的生成函数 \[T(x)=x+\prod_{i\in D}T^i(x)\] \[T(x)-\prod_{i\in D} ...
- IP 基础知识全家桶,45 张图一套带走
前言 前段时间,有读者希望我写一篇关于 IP 分类地址.子网划分等的文章,他反馈常常混淆,摸不着头脑. 那么,说来就来!而且要盘就盘全一点,顺便挑战下小林的图解功力,所以就来个 IP 基础知识全家桶. ...
- vue全家桶(Vue+Vue-router+Vuex+axios)(Vue+webpack项目实战系列之二)
Vue有多优秀搭配全家桶做项目有多好之类的咱就不谈了,直奔主题. 一.Vue 系列一已经用vue-cli搭建了Vue项目,此处就不赘述了. 二.Vue-router Vue的路由,先献上文档(http ...
随机推荐
- 关于jupyter notebook
直接点击进行跳转阅读:https://zhuanlan.zhihu.com/p/33105153
- nyoj 911 Registration system(map)
Registration system 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描述 A new e-mail service "Berlandesk&q ...
- HDU 1234 简单模拟题
题目很简单不多说了,我只是觉得这题目的输入方式还是很有特点的 #include <cstdio> #include <cstring> #include <algorit ...
- C语言试题
C语言试题 [说明]: 1.本试题中不考虑头文件引用问题(假定已经包含正确的头文件),C语言的标准函数都可用: 2.如果不特别说明,假定程序运行环境为:操作系统Windows 2000, VC6.0编 ...
- NOIP2015 提高组合集
NOIP 2015 提高组 合集 D1 T1 神奇的幻方 题目让你干啥你就干啥,让你咋走你就咋走就完事儿了 #include <iostream> #include <cstdio& ...
- 时间插件,js格式化,js某月天数,js某月最后一天日期
//时间格式化 Date.prototype.format = function(fmt) { var o = { "M+": this.getMonth() + 1, //月份 ...
- 学习node js 之微信公众帐号接口开发 准备工作
绪:因工作须要,近期開始学习Node js,之前隐隐约约听到过node js 但没有实际见到过,仅仅好google了:至于什么是node js,能做些什么,有多么好.或者有哪些弊端我这里就不多说了,由 ...
- poj 3090 && poj 2478(法雷级数,欧拉函数)
http://poj.org/problem?id=3090 法雷级数 法雷级数的递推公式非常easy:f[1] = 2; f[i] = f[i-1]+phi[i]. 该题是法雷级数的变形吧,答案是2 ...
- struts2国际化---配置国际化全局资源文件并输出国际化资源信息
我们首先学习怎么配置国际化全局资源文件.并输出资源文件信息 1.首先struts2项目搭建完毕后,我们在src文件夹下.即struts2.xml同级文件夹下创建资源文件.资源文件的名称格式为: XXX ...
- 关于PROFIBUS Master(H)不能正确识别并处理 DP-Slave 回复的RS帧的一些思考
图1.是在測试过程中,发现PROFIBUS Master(H)不能正确识别并处理 DP-Slave 回复的RS帧.引起Slave回复 RS 帧的操作是"断开Slave与Master之间的PR ...