多项式全家桶(还有些不会的)
多项式全家桶
记号约定
- :对 求 阶导。
- : 的 次项系数。
多项式微积分
我们知道:
以及可加性,于是容易写出代码:
#define rep(x,y,z) for(int x=(y);x<=(z);x++)
#define per(x,y,z) for(int x=(y);x>=(z);x--)
void PolyDer(ll* a, ll* b, ll n) {
rep(i, 1, n-1) b[i-1] = a[i] * i % mod;
b[n-1] = 0;
}
void PolyInt(ll* a, ll* b, ll n) {
rep(i, 1, n-1) b[i] = a[i-1] * inv(i) % mod;
b[0] = 0;
}
时间复杂度 。
多项式加减法
对应项相加减即可,于是我都没封装。
时间复杂度 。
多项式乘法
前置知识:FFT/NTT。其中 FFT 用于无模数情况,NTT 用于有 NTT 模数情况。
ll k = 1;
for(;k<=n+m;k<<=1);
NTT(a, k, 1); NTT(b, k, 1);
rep(i, 0, k-1) a[i] = a[i] * b[i] % mod;
NTT(a, k, -1);
时间复杂度为 NTT 的 。
任意模数多项式乘法
还不会。
多项式牛顿迭代
前置知识:牛顿迭代、泰勒展开。请先自行学习。
多项式牛顿迭代并不会被封装,但它是一个十分重要的推导方法,下面都会用到。
多项式牛顿迭代用于解决这样的问题:给定多项式 ,求多项式 使得 。
首先, 也就是 的解需要单独求出。
然后设 是 的解。
对 在 处进行泰勒展开:
因为 的最低非零项次数最低为 ,所以 ,进一步得到:
多项式乘法逆元
设我们要求给定函数 的乘法逆元,则有方程:
应用多项式牛顿迭代可得:
void PolyInv(ll* a, ll* b, ll n) {
if(n == 1) {b[0] = inv(a[0]); return;}
PolyInv(a, b, (n+1)>>1);
ll k = 1;
for(;k<=(n<<1);k<<=1);
rep(i, 0, k-1) tmp[i] = i < n ? a[i] : 0;
NTT(b, k, 1); NTT(tmp, k, 1);
rep(i, 0, k-1) b[i] = b[i] * (2 - b[i] * tmp[i] % mod + mod) % mod;
NTT(b, k, -1);
rep(i, 0, k-1) b[i] = i < n ? b[i] : 0;
}
时间复杂度为 。
多项式开根
设我们要求 满足 。
首先在 时需要单独求出。如果题目保证了 ,则可以直接 ,否则需要使用 二次剩余。
有方程:
应用多项式牛顿迭代可得:
void PolySqrt(ll* a, ll* b, ll n) {
// if(n == 1) {b[0] = 1; return;}
if(n == 1) {mt19937 rnd(time(0)); b[0] = cipolla(rnd, a[0])[0]; return;}
PolySqrt(a, b, (n+1)>>1);
ll k = 1;
for(;k<=(n<<1);k<<=1);
rep(i, 0, k-1) finv[i] = 0;
PolyInv(b, finv, n);
rep(i, 0, k-1) tmp[i] = i < n ? a[i] : 0;
NTT(b, k, 1); NTT(tmp, k, 1); NTT(finv, k, 1);
rep(i, 0, k-1) b[i] = (tmp[i] * finv[i] % mod + b[i]) % mod * inv2 % mod;
NTT(b, k, -1);
rep(i, 0, k-1) b[i] = i < n ? b[i] : 0;
}
时间复杂度为 。
多项式对数函数
对于多项式 ,若 存在,则 。
遇事不决先求导:
然后两边积分:
void PolyLn(ll* a, ll* b, ll n) {
ll k = 1;
for(;k<=(n<<1);k<<=1);
rep(i, 0, k-1) finv[i] = 0;
PolyInv(a, finv, n);
rep(i, 0, k-1) tmp[i] = 0;
PolyDer(a, tmp, n);
NTT(tmp, k, 1); NTT(finv, k, 1);
rep(i, 0, k-1) tmp[i] = tmp[i] * finv[i] % mod;
NTT(tmp, k, -1);
PolyInt(tmp, b, n);
}
时间复杂度为 。
多项式指数函数
对于多项式 ,若 存在,则 ,否则 的常数项不收敛。
有方程:
应用多项式牛顿迭代可得:
void PolyExp(ll* a, ll* b, ll n) {
if(n == 1) {b[0] = 1; return;}
PolyExp(a, b, (n+1)>>1);
ll k = 1;
for(;k<=(n<<1);k<<=1);
rep(i, 0, k-1) gln[i] = 0;
PolyLn(b, gln, n);
rep(i, 0, k-1) tmp[i] = 0;
rep(i, 0, n-1) tmp[i] = ((!i) - gln[i] + a[i] + mod) % mod;
NTT(tmp, k, 1); NTT(b, k, 1);
rep(i, 0, k-1) b[i] = b[i] * tmp[i] % mod;
NTT(b, k, -1);
}
时间复杂度为 。
多项式带余除法
已知 ,求 除以 得到的商 和余数 。
如果可以消除余数 的影响,则多项式求逆直接就解决了。
构造变换:
也就是翻转 的各项系数。
我们记 。
将 中的 替换为 ,并把两边乘以 ,得到:
则:
使用多项式求逆可以得到 ,代回原式可得 。
void PolyDiv(ll* a, ll* b, ll* q, ll* r, ll n, ll m) {
rep(i, 0, n-1) gr[i] = i < m ? b[m-1-i] : 0;
PolyInv(gr, finv, n-m+1);
ll k = 1;
for(;k<=((n-m+1)<<1);k<<=1);
rep(i, 0, n-1) fr[i] = i < n - m + 1 ? a[n-1-i] : 0;
NTT(finv, k, 1); NTT(fr, k, 1);
rep(i, 0, k-1) tmp[i] = finv[i] * fr[i] % mod;
NTT(tmp, k, -1);
if(q) {
rep(i, 0, n-m) q[i] = tmp[n-m-i];
if(r) {
reverse(tmp, tmp+n-m+1);
reverse(gr, gr+m);
k = 1;
for(;k<=n;k<<=1);
rep(i, 0, k-1) tmp[i] = i <= n - m ? tmp[i] : 0;
rep(i, 0, k-1) gr[i] = i < m ? gr[i] : 0;
NTT(tmp, k, 1); NTT(gr, k, 1);
rep(i, 0, k-1) tmp[i] = tmp[i] * gr[i] % mod;
NTT(tmp, k, -1);
rep(i, 0, m-2) r[i] = (a[i] - tmp[i] + mod) % mod;
}
}
}
时间复杂度为 。
多项式幂函数
普通的多项式快速幂计算 的复杂度为 。
当 时,有:
当 时,我们想到除以 把 变成 。不过当 时会出问题,我们需要找到最低非零项 ,则:
先放一个前者的代码,后者还没写:
void PolyPow(ll* a, ll* b, ll n, ll k) {
PolyLn(a, fln, n);
rep(i, 0, n-1) fln[i] = fln[i] * k % mod;
PolyExp(fln, b, n);
}
时间复杂度为 。
多项式三角函数
由欧拉公式 ,我们知道:
代入 得:
然后 可以得到正切函数。
那这里的 是什么?由定义 ,令 为 模意义开根的结果即可,得到 或者 。
void PolyTri(ll* a, ll* sin, ll* cos, ll* tan, ll n) {
ll imunit = qpow(g, (mod-1)>>2);
rep(i, 0, n-1) tmp2[i] = a[i] * imunit % mod;
rep(i, 0, n-1) foppo[i] = (mod - a[i]) % mod * imunit % mod;
PolyExp(tmp2, fexp, n); PolyExp(foppo, gexp, n);
if(sin) rep(i, 0, n-1) sin[i] = (fexp[i] - gexp[i] + mod) % mod * inv(2*imunit) % mod;
if(cos) rep(i, 0, n-1) cos[i] = (fexp[i] + gexp[i]) % mod * inv2 % mod;
if(sin && cos && tan) {
PolyInv(cos, finv, n);
ll k = 1;
for(;k<=(n<<1);k<<=1);
NTT(sin, k, 1); NTT(finv, k, 1);
rep(i, 0, k-1) tan[i] = sin[i] * finv[i] % mod;
NTT(sin, k, -1); NTT(tan, k, -1);
rep(i, 0, k-1) tan[i] = i < n ? tan[i] : 0;
}
}
时间复杂度为 。
多项式反三角函数
遇事不决先求导:
代入 得:
void PolyATri(ll* a, ll* asin, ll* acos, ll* atan, ll n) {
ll k = 1;
for(;k<=(n<<1);k<<=1);
rep(i, 0, k-1) f2[i] = i < n ? a[i] : 0;
NTT(f2, k, 1);
rep(i, 0, k-1) f2[i] = f2[i] * f2[i] % mod;
NTT(f2, k, -1);
if(asin) {
rep(i, 0, k-1) tmp2[i] = ((!i) - f2[i] + mod) % mod, fsqrt[i] = 0;
PolySqrt(tmp2, fsqrt, n);
rep(i, 0, k-1) tmp3[i] = 0;
PolyInv(fsqrt, tmp3, n);
rep(i, 0, k-1) tmp[i] = 0;
PolyDer(a, tmp, n);
NTT(tmp, k, 1); NTT(tmp3, k, 1);
rep(i, 0, k-1) tmp[i] = tmp[i] * tmp3[i] % mod;
NTT(tmp, k, -1);
PolyInt(tmp, asin, n);
}
if(acos) {
PolyATri(a, acos, nullptr, nullptr, n);
rep(i, 0, n-1) acos[i] = (mod - acos[i]) % mod;
}
if(atan) {
rep(i, 0, k-1) tmp2[i] = ((!i) + f2[i]) % mod, tmp3[i] = 0;
PolyInv(tmp2, tmp3, n);
rep(i, 0, k-1) tmp[i] = 0;
PolyDer(a, tmp, n);
NTT(tmp, k, 1); NTT(tmp3, k, 1);
rep(i, 0, k-1) tmp[i] = tmp[i] * tmp3[i] % mod;
NTT(tmp, k, -1);
PolyInt(tmp, atan, n);
}
}
时间复杂度为 。
多项式多点求值
还不会。
多项式快速插值
还不会。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
2021-07-12 从零开始重学动态规划(最后更新:2021.10.3)
2021-07-12 【洛谷 P3396 哈希冲突】解题报告(根号分治)