多项式笔记
多项式笔记
本文为阅读 OI-Wiki 书籍 学长slide 网上博客的笔记
多项式一般用来对一些函数的计算加速。
前置知识
多项式插值
复数
复数运算
已知
辐角和模长
复数的模与辐角是复数三角形式表示的两个基本元素,复数所对应的向量长度称为复数的幅值,该向量与实轴正方向的夹角为复数的辐角。辐角的大小有无穷多,但是辐角主值唯一确定。利用复数的模和辐角,可以将复数表示成三角表示式和指数表示式,并可以和代数表示式之间互相转化,以方便讨论不同问题时的需要。 ----百度百科
复数表示
代数表示法:
由辐角和模长定义可知三角表示法:
由欧拉公式可知
指数表示法:
从指数表示法我们就可以看出, 复数乘法就是模长相乘, 辐角相加。
单位根
引理1:任意一个复数系一元
次多项式方程在复数域上恰好 个根。
我们记
这式子可以从复数乘法的几何意义来来理解, 模长必定为1, 辐角乘
性质:
这些性质的证明: 都可以通过几何意义来理解。
快速傅里叶变换 FFT
算法思路
我们知道
但现在就有一个问题, 我们怎么实现快速插值并且将插值快速变换为多项式呢?
DFT
考虑我们要快速求出某个函数
将
考虑单位根有这样的性质,
和:
考虑我们用总共
IDFT
现在我们要从插值还原到多项式的系数。
考虑从线性代数的角度理解:
左边向量为插值, 右边向量为系数。 考虑我们要求出系数, 就是给插值向量左乘一个
考虑
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
const db pi = acos(-1);
const int N = 3e6 + 10;
struct C{
db x, y;
C() { x = y = 0; }
C(db _x, db _y) : x(_x), y(_y) {}
inline C operator + (const C &a) { return C(x + a.x, y + a.y); }
inline C operator - (const C &a) { return C(x - a.x, y - a.y); }
inline C operator * (const C &a) { return C(x * a.x - y * a.y, x * a.y + y * a.x); }
};
int n, m, lim, bit, rev[N];
void FFT(vector<C> &v, int op) {
for(int i = 0; i < lim; i++) if(i < rev[i]) swap(v[i], v[rev[i]]);
for(int k = 1; k < lim; k <<= 1) {
C omega(cos(pi / k), op * sin(pi / k));
for(int i = 0; i < lim; i += (k << 1)) {
C w(1, 0);
for(int j = 0; j < k; j++, w = w * omega) {
C s = v[i + j], t = v[i + j + k] * w;
v[i + j] = s + t, v[i + j + k] = s - t;
}
}
}
if(op == -1) for(int i = 0; i < lim; i++) v[i].x /= lim;
}
int main() {
scanf("%d%d", &n, &m);
lim = 1 << (32 - __builtin_clz(n + m)), bit = 32 - __builtin_clz(n + m);
vector<C> A(lim), B(lim);
for(int i = 0; i <= n; i++) scanf("%lf", &A[i].x);
for(int i = 0; i <= m; i++) scanf("%lf", &B[i].x);
for(int i = 0; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
FFT(A, 1); FFT(B, 1);
for(int i = 0; i < lim; i++) A[i] = A[i] * B[i];
FFT(A, -1);
for(int i = 0; i <= n + m; i++) printf("%lld ", (long long)(A[i].x + 0.5));
return 0;
}
快速数论变换 NTT
NTT 和 FFT 十分相似, 常见于多项式计算需要取模时使用, 考虑在
其他和FFT非常类似, 就不写了。
多项式牛顿迭代
泰勒展开
就是一个用多项式逼近某个函数的公式, 非常的牛, 我也不会, 背住就好了。
牛顿迭代解决这样的一个问题:
给定多项式
求出模
算法
from OI_WIKI
将
因为
则:
这里需要求导:
所以比较常用的是这几个:
复合函数求导:
多项式求逆
带入公式即可。
多项式开根
多项式求导
就按照上面的求导规则即可,
多项式积分
把求导反着来。
多项式ln
多项式exp
快速沃尔什变换 FWT
给两个长为
我们参照 FFT, 通过插值来变换, 但是这里显然不能插值, 但是我们可以借鉴思想。 构造某种变换, 使得其可以快速变换和反演, 这也是算法精髓所在。
or
接下来我们讲 or 的做法。
我们构造
那么我们就有
那么现在就考虑怎么快速得到
现在我们就考虑怎么将
那么就是
void OR(int *v, int type = 1) {
for(int lim = 2, k = 1; lim <= n; lim <<= 1, k <<= 1)
for(int i = 0; i < n; i += lim)
for(int j = 0; j < k; j++)
v[i + j + k] = add(v[i + j + k], mul(v[i + j], type));
}
参考代码理解, 写的迭代形式。
and
考虑仿照上面的做法,
xor
对于 xor,
拉格朗日插值法
如果 $$
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下