多项式基础操作学习笔记
多项式简单操作学习笔记
前置知识与约定
- 多项式乘法,我们可以用
FFT/NTT
解决. - 简单求导/微积分(
简单的意思是如果这都不会就不要说自己学过了) - \([P]\)表示艾弗森括号,当\(P\)为真时表达式的值为\(1\),\(P\)为假时表达式的值为\(0\)
- \(\Gamma\)表示阶乘函数
- \(f^{(n)}\)表示函数\(f(x)\)的\(n\)阶导函数
多项式乘法
我们可以用FFT/NTT
在\(\mathcal O(n \log n)\)的时间内解决, 但两者优劣势不尽相同.
FFT
的缺点在于使用复数,精度差,使用单位根,常数大等.
而NTT
的缺点在于必须在模数的原根较特殊的情况下才能使用(如\(998244353\)等).
Code
inline void NTT(int *f, int type) {
for(int i = 0; i < n; ++i) if(i < rev[i]) std::swap(f[i], f[rev[i]]);
for(int i = 1; i < n; i <<= 1) {
int tk = quick_power(3, (mod - 1) / (i << 1));
for(int j = 0; j < n; j += (i << 1)) {
int t = 1, x, y;
for(int k = 0; k < i; ++k, t = 1ll * t * tk % mod) {
x = f[j + k], y = 1ll * t * f[j + k + i] % mod;
f[j + k] = (x + y) % mod; f[j + k + i] = (x - y + mod) % mod;
}
}
}
if(type == 1) return ;
int inv = quick_power(n, mod - 2);
std::reverse(f + 1, f + n);
for(int i = 0; i < n; ++i) f[i] = 1ll * f[i] * inv % mod;
}
多项式泰勒展开
如果函数\(f(x)\)在\(x_0\)处存在\(n\)阶导数,那么定义\(f(x)\)在\(x = x_0\)处的\(\rm Taylor\)展开式为:
\[f(x) = \sum_{i=0}^n \frac {f^{(n)}(x_0)}{\Gamma(i)} (x - x_0)^i + R_n(x)\]
其中\(R_n(x)\)表示拉格朗日余项,即\((x - x_0)^n\)的高阶无穷小.
一个例子:\(e^x = 1 + \frac {x}{\Gamma(1)} + \frac {x^2}{\Gamma(2)} + \cdots\)
多项式牛顿迭代
牛顿迭代可以用来求函数的零点,而多项式牛顿迭代可以求多项式函数的零点.
即对于一个多项式\(F(x)\), 求一个多项式\(G_k(x)\)满足\(F(G_k(x)) \equiv 0 \pmod{2^k}\)
假设我们已经求出了\(G_{k-1}(x)\),考虑如何推得\(G_k(x)\)
我们把\(F(G_k(x))\)在\(x = G_{k-1}(x)\)处进行\(\rm Taylor\)展开,可以得到:
\[F(G_k(x)) = F(G_{k-1}(x)) + \frac {F'(G_{k-1}(x))}{\Gamma(1)} (G_k(x) - G_{k-1}(x))\]
化简,移项得:
\[G_k(x) = G_{k-1}(x) - \frac {F(G_{k-1}(x))}{F'(G_{k-1}(x))}\]
多项式求逆
已知\(F(x)\),求\(G(x)\)满足\(F(x)G(x) \equiv 1 \pmod{x^n}\)
和推牛顿迭代的式子一样,我们假设已经求出了\(G_0(x)\)满足:
\[F(x)G_0(x) \equiv 1 \pmod{x^{\lfloor \frac {2}{n}\rfloor}}\]
由题目条件可得:
\[F(x)G(x) \equiv 1 \pmod{x^{\lfloor \frac {2}{n}\rfloor}}\]
上下两式相减可得:
\[F(x)G_0(x) - F(x)G(x) \equiv 0 \pmod{x^{\lfloor \frac {2}{n}\rfloor}}\]
同余式两边同时除以\(F(x)\)可得:
\[G_0(x) - G(x) \equiv 0 \pmod{x^{\lfloor \frac {2}{n}\rfloor}}\]
同余式两遍平方可得:
\[G_0(x)^2 - 2G(x)G_0(x) + G(x)^2 \equiv 0 \pmod{x^n}\]
由题目条件,同余时两边同时乘\(F(x)\)得:
\[F(x)G_0(x)^2 - 2G_0(x) + G(x) \equiv 0 \pmod{x^n}\]
把式子移项,可得:
\[G(x) = 2G_0(x) - F(x)G_0(x)^2 \equiv 0 \pmod{x^n}\]
递归求解即可.
Code
inline void PolyInv(int *f, int *g, int len) {
if(len == 1) {g[0] = quick_power(f[0], mod - 2); return ;}
PolyInv(f, g, (len + 1) >> 1);
n = 1, m = 0;
while(n < (len << 1)) {n <<= 1; ++m;}
for(int i = 1; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (m - 1));
for(int i = 0; i < len; ++i) c[i] = f[i];
for(int i = len; i < n; ++i) c[i] = 0;
NTT(c, 1); NTT(g, 1);
for(int i = 0; i < n; ++i) g[i] = 1ll * (2 - 1ll * c[i] * g[i] % mod + mod) % mod * g[i] % mod;
NTT(g, -1);
for(int i = len; i < n; ++i) g[i] = 0;
}
多项式求导
没啥好说的吧, 给出公式:
\[x^{a^{\prime}} = ax^{a-1}\]
Code
inline void PolyDer(int *f, int *g, int len) {
for(int i = 1; i < len; ++i) g[i - 1] = 1ll * i * f[i] % mod;
g[len - 1] = 0;
}
多项式积分
根据牛顿-莱布尼兹公式:
\[\int x^a {\rm d}x = \frac {1}{a+1} x^{a+1}\]
Code
inline void PolyInt(int *f, int *g, int len) {
for(int i = 1; i < len; ++i) g[i] = 1ll * f[i - 1] * quick_power(i, mod - 2) % mod;
g[0] = 0;
}
多项式对数函数
即多项式求\(\ln\)
已知\(F(x)\),求\(G(x)\)满足\(G(x) \equiv \ln F(x) \pmod{x^n}\)
设函数\(H(x) = \ln x\),那么同余式右边即为\(H(F(x))\).
考虑将同余式两边求导,可得:
\[G'(x) \equiv H'(x) \pmod{x^n}\]
即:
\[G'(x) \equiv \frac{F'(x)}{F(x)} \pmod{x^n}\]
那么我们把\(F(x)\)求个导,再求个逆,然后把得到的两个多项式求一下卷积.
这样我们得到的是\(G'(x)\),然后再把他积回去即可.
Code
inline void PolyLn(int *f, int *g, int len) {
PolyDer(f, a, len); PolyInv(f, b, len);
n = 1; m = 0;
while(n < (len << 1)) {n <<= 1; ++m;}
for(int i = 1; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (m - 1));
NTT(a, 1); NTT(b, 1);
for(int i = 0; i < n; ++i) a[i] = 1ll * a[i] * b[i] % mod;
NTT(a, -1); PolyInt(a, g, len);
}
多项式指数函数
即多项式求\(\rm exp\)
已知\(F(x)\),求\(G(x)\)满足\(G(x) \equiv \exp (F(x)) \pmod{x^n}\).
考虑同余式两边同时取自然对数:
\[\ln G(x) \equiv F(x) \pmod{x^n}\]
移项,可得:
\[\ln G(x) - F(x) \equiv 0 \pmod{x^n}\]
将这个式子套进 多项式牛顿迭代
可得:
\[G(x) \equiv G_0(x) - \frac {\ln G_0(x) - F(x)}{\frac {1}{G_0(x)}}\]
化简可得:
\[G(x) \equiv G_0(x) - (\ln G_0(x) - F(x)) \times G_0(x) \pmod {x^n}\]
\[G(x) \equiv G_0(x) \times (1 - \ln G_0(x) + F(x)) \pmod {x^n}\]
递归+多项式Ln
计算即可.
Code
inline void PolyExp(int *f, int *g, int len) {
if(len == 1) {g[0] = 1; return ;}
PolyExp(f, g, (len + 1) >> 1);
n = 1; m = 0;
while(n < (len << 1)) {n <<= 1; ++m;}
for(int i = 1; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (m - 1));
for(int i = 0; i < (len << 1); ++i) d[i] = e[i] = 0;
PolyLn(g, d, len);
for(int i = 0; i < len; ++i) e[i] = f[i];
NTT(g, 1); NTT(d, 1); NTT(e, 1);
for(int i = 0; i < n; ++i) g[i] = (1ll - d[i] + e[i] + mod) * g[i] % mod;
NTT(g, -1);
for(int i = len; i < n; ++i) g[i] = 0;
}
多项式快速幂
已知\(F(x)\),求\(G(x)\)满足\(G(x) \equiv F(x)^k \pmod{x^n}\)
首先我们在初中阶段就应该学过这样一个式子:
\[\log_a b^k = k \log_a b\]
这个式子对多项式也成立,
那么我们只需要把\(F(x)\)求一个\(\ln\),然后把它乘上\(k\),最后再\(\exp\)回去即可.
Code
inline void PolyPow(int *f, int *g, int k, int len){
PolyLn(f, h, len);
for(int i = 0; i < len; ++i) h[i] = 1ll * h[i] * k % mod;
PolyExp(h, g, len);
for(int i = 0; i < len; ++i) h[i] = 0;
}
多项式开根
已知\(F(x)\),求\(G(x)\)满足\(G(x)^2 \equiv F(x) \pmod{x^n}\)
和多项式求逆类似,利用那个结论,得到:
\[2 \ln G(x) \equiv \ln F(x) \pmod{x^n}\]
那么我们先把\(F(x)\)求个\(\ln\),然后把它的系数乘以\(2\)的逆元,最后再\(\exp\)回去即可.
Code
inline void PolySqr(int *f, int *g, int len) {
PolyLn(f, l, len);
int invt = quick_power(2, (mod - 2));
for(int i = 0; i < len; ++i) l[i] = 1ll * l[i] * invt % mod;
PolyExp(l, g, len);
for(int i = 0; i < len; ++i) l[i] = 0;
}
更新日志&引用
- 2019/9/27, 更新至多项式求逆
- 2019/9/28, 更新至多项式开根
- 2019/9/28, To do 多项式除法&取模
作者是完完全全跟着Venus和M-sea大爷学的多项式,所以本篇文章有很多引用来自她们的博客.
再次感谢这两篇博客给予本菜鸡在多项式学习方面的帮助!
摘要来自NaCly_Fish的Luogu个人空间.
To be continue
原文地址:https://www.cnblogs.com/herself32-lyoi/p/11610362.html
- [信息安全] 2.密码工具箱(续)
- 脑科学发展的助推器
- BFIThumb:WordPress 中替代TimThumb 进行裁图的选择
- jquery 操作ajax 相关方法
- SQL SERVER 2008 Hierarchyid数据类型
- Html5 学习利器 Web Standards Update for Microsoft Visual Studio 2010 SP1
- MongoDB 客户端 MongoVue
- HttpClient介绍
- 10个使用 Foundation 框架开发的WordPress 主题推荐
- jQuery 效果使用
- 几款更换WordPress 后台UI 的插件推荐
- 入门:构建简单的Web API
- WordPress 编辑器快捷键——让写作来得更方便些吧!
- ASP.NET Web API: 宿主(Hosting)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 这一次搞懂Spring代理创建及AOP链式调用过程
- 这一次搞懂Spring事务注解的解析
- 这一次搞懂Spring事务是如何传播的
- 这一次搞懂SpringMVC原理
- 这一次搞懂Spring Web零xml配置原理以及父子容器关系
- 这一次搞懂SpringBoot核心原理(自动配置、事件驱动、Condition)
- 全网最深分析SpringBoot MVC自动配置失效的原因
- Mybatis源码初探——优雅精良的骨架
- 深入Mybatis源码——配置解析
- 深入Mybatis源码——执行流程
- Mybatis插件扩展以及与Spring整合原理
- 你所不知道的Spring的@Autowired实现细节
- modbus-RTU-crc16——c语言
- KEIL 生成 Binaxf 文件
- Istio可观测性