多项式全家桶
一、前置芝士
1. 基本概念
- 多项式: 有限项相加的求和式
,记作 - 多项式的度(次数):对于一个多项式
,称其最高次项的次数为该多项式的度( ),也称次数,记作 - 级数:将数列的项依次用加号连接起来的函数
- 幂级数:每项均为非负整数次幂函数乘以常数系数的级数称为幂级数
更多&拓展
对于多项式,我们也可以将其直接定义为一个系数序列,表示为:
此处我们认为,
若支持无穷项,则称
Q:多项式和级数的区别?
A:多项式是有限的,级数是无限的
2. 多项式的表示方法
① 系数表示法
存储方式
存储时只存储每一位的系数即可
② 点值表示法
即把多项式放入平面直角坐标系中,得到一个函数图像
存储方式
通常地,带入
计算多项式乘法时:点值表示法可以
而这两种表示法可以相互转化,但是暴力转化是 FFT
优化多项式乘法,并且它是
3. 复指数函数 & 复三角函数
- 复指数函数:对于复数
,定义 为其复指数函数。特别地,我们通常将 记作
在代数上
- 复三角函数:
4. 复数的三种形式
详细见 复数 - ricky_lin
① 代数形式
适用范围:计算复数的加减乘除
② 三角形式
③ 指数形式
适用范围:这两种形式用于计算复数的乘除两个运算以及后面的运算较为方便
三角形式
知识层面要求更低,但 指数形式
会更加地方便
5. 单位根
本小节需要掌握的前置知识点
- 复数
- 欧拉定理
① 定义和约定
我们定义
这
根据学过的知识
那么
一般地,我们默认
② 性质和证明
- 性质1:
证明
由定义- 性质2:
证明
- 性质3:
证明
我们先需要知道一件事:
然后:
二、多项式乘法
上面我们讲到多项式若用点值表示,那么乘法是
然而,对于系数转点值,需要代入
然后我们考虑,找一些
但是,显然地,这样的
1. FFT——快速傅里叶变换
① 递归
第一种方法是将
令
可以得到:
代入
代入
然后便可以递归求解,复杂度
重要提示
考虑上面的式子的适用条件是每一次多项式的长度都是
② 倍增
但是考虑递归常数过大,我们考虑倍增怎么做
递归的时候需要把系数按照 位逆序变换
:
例子
以
状态 | 序列 |
---|---|
初始序列 | |
第一次二分 | |
第二次二分 | |
第三次二分 |
根据上面的例子,我们可以找到一个规律:
初始序列的下标在二进制下 翻转便可以得到其在最终序列中的下标
例子
以
位逆序变换实现
位逆序置换可以
我们设
首先
我们从小到大求
其它位的翻转结果,把最后一位
数学语言:
③ 蝶形运算优化
我们从上面知道,我们需要通过下面两个式子进行推导:
使用 位逆序置换
后,对于给定的
的值存储在下标为 的位置上, 的值存储在下标为 的位置上 的值存储在数组下标为 , 的值将存在数组下标为 的位置
所以说我们每次变换的时候不需要再新开一个数组,只需要在原数组上进行覆写,即可。
这样我们就完成了 DFT——离散傅里叶变换
④ IDFT——快速傅里叶逆变换
在前面,我们将两个多项式用
现在,我们需要将点值表示法在
方法即为:将 DFT
中,再对其所有系数都除以 len
(多项式长度)
证明
我们知道
同时我们也知道了一个函数
代入
记
由上式得到,当
代回原式:
code
#include<cstdio> #include<vector> #include<cmath> using namespace std; typedef long long ll; const double PI = acos(-1); struct Complex{ double x,y; Complex(double x = 0,double y = 0): x(x),y(y){}; Complex operator + (const Complex &B){return Complex(x+B.x,y+B.y);} Complex operator - (const Complex &B){return Complex(x-B.x,y-B.y);} Complex operator * (const Complex &B){return Complex(x*B.x-y*B.y,x*B.y+y*B.x);} }; typedef vector<Complex> Poly; Poly A,B; int len = 1,llog; void FFT(vector<int> &rev,Poly &v,int inv){ for(int i = 0; i < len; ++i) if(i < rev[i]) swap(v[i],v[rev[i]]); for(int k = 1; k < len; k <<= 1){ Complex omega(cos(PI/k), inv * sin(PI/k)); for(int i = 0; i < len; i += k * 2){ Complex w(1,0); for(int j = 0; j < k; ++j, w = w * omega){ Complex s = v[i+j], t = v[i+j+k] * w; v[i+j] = s + t; v[i+j+k] = s - t; } } } // for(int i = 0; i < len; ++i) printf("%lf ",v[i].x);puts(""); if(inv == -1) for(int i = 0; i < len; ++i) v[i].x /= len; } int n,m; int main() { scanf("%d%d",&n,&m); while(len <= n + m) len <<= 1,++llog; A.resize(len);B.resize(len); for(int i = 0; i <= n; ++i) scanf("%lf",&A[i].x); for(int i = 0; i <= m; ++i) scanf("%lf",&B[i].x); vector<int> rev(len); for(int i = 0; i < len; ++i) rev[i] = (rev[i>>1] >> 1) | ((i&1) << (llog-1)); FFT(rev, A, 1), FFT(rev, B, 1); for(int i = 0; i < len; ++i) A[i] = A[i] * B[i]; FFT(rev, A, -1); for(int i = 0; i <= n + m; ++i) printf("%lld ",(ll)(A[i].x + 1e-3)); return 0; }
⑤ 从线性代数角度理解FFT
DFT
本身是个线性变换,可以理解为将目标多项式当作向量,左乘一个矩阵得到变
换后的向量,以模拟把单位复根代入多项式的过程:
现在我们已经得到最左边的矩阵
所以,根据矩阵的基础知识,我们只要在式子两边左乘中间那个大矩阵
由于这个矩阵的元素非常特殊,它的逆矩阵也有特殊的性质,就是每一项取倒数,再除以变换的长度 n,就能得到它的逆矩阵。
注:这里的长度
2. NTT——快速数论变换
由于 FFT
算
于是我们便有了 FFT
① 补充亿点芝士
阶
- 若
互素,且 ,对于 最小的 ,我们称之为 模 的阶,记作 ,例如
性质:
证明
反证法:
若
显然地,
原根
设
换一个角度理解
就是说
可以得到一个结论
原根判定定理 & 原根计算
设
对于
没什么快速求法,只能暴力枚举判断。若素数
其中
通常模数常见的有
我们用原根可以代替 FFT 中的
证明
- 性质1:
显然地
, 根据费马小定理
,得证 ,得证。
- 性质2:
由定义得:
- 性质3:
又因为
,所以 两两不同 所以
即证:
在实现 NTT
的时候,把 DFT
中的 IDFT
就是将
例题:P3723 [AH2017/HNOI2017] 礼物
给定两个长为 n 的数列 a, b,a, b 均可循环移动,你需要选择一个整数 c,最小化
solution
把柿子拆一拆:前三项都是关于一个 的二次函数,那么前三项通过 最小化即可,现在我们需要做的,便是最大化 我们先倍长
,断环为链: 因为此处
之差不变且为 ,所以我们使用减法卷积 将
数组逆序,得到: 然后直接
NTT
即可
三、多项式基本运算
1. 前置知识
本节只列出定义和常见公式,详细请自行查询相关资料学习
① 求导
② 泰勒展开
对于一个函数
2. 多项式牛顿迭代
牛顿迭代解决了以下 problem:
给定多项式
,已知有 求出模
意义下的 solution
考虑倍增首先当
是时 的解需要单独求出。 假设现在已经得到了模
意义下的解 ,要求模 意义下的解 将
在 处进行泰勒展开,有: 因为
的最低非零项次数最低为 ,所以: 那么:
3. 多项式求逆
设给定函数为
,求 使得 solution
由上式可得:使用牛顿迭代得到: 时间复杂度:
4. 多项式求ln、exp
① 定义
我们先了解
我们先泰勒展开,可以得到:
② 多项式求ln
设给定函数为
两边同时求导,得:
最后使用多项式求逆,并且积分积回去即可:
③ 多项式exp
设给定函数为
由上式可得:
使用牛顿迭代得:
时间复杂度:
5. 多项式开方
设给定函数为
由上式可得:
使用牛顿迭代得:
时间复杂度:
边界条件需要求一个数在模意义下的二次剩余,需使用
6. 多项式除法&取模
给定多项式
发现若能消除
考虑构造变换:
即,
设
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/blog_summary_polynomial.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步