多项式学习笔记
符号与约定
若无特殊说明,多项式的大小均默认为
我们定义
我们使用
基础模板
const int kM = 998244353;
struct Mi {
int v;
Mi(int v = 0) : v(v) {}
Mi operator=(int v) { return *this = Mi(v); }
Mi operator+(const Mi &o) const { return Mi(v + o.v - (v + o.v >= kM ? kM : 0)); }
Mi operator-(const Mi &o) const { return Mi(v - o.v + (v - o.v < 0 ? kM : 0)); }
Mi operator*(const Mi &o) const { return Mi(1LL * v * o.v % kM); }
Mi operator~() const;
Mi operator/(const Mi &o) const { return *this * ~o; }
Mi operator+=(const Mi &o) { return *this = *this + o; }
Mi operator-=(const Mi &o) { return *this = *this - o; }
Mi operator*=(const Mi &o) { return *this = *this * o; }
Mi operator/=(const Mi &o) { return *this = *this / o; }
};
istream &operator>>(istream &in, Mi &o) {
LL v;
in >> v;
o = v;
return in;
}
ostream &operator<<(ostream &out, const Mi &o) { return out << o.v; }
Mi P(Mi b, int e) {
Mi s = 1;
for (; e; e >>= 1, b *= b) {
if (e & 1) {
s *= b;
}
}
return s;
}
Mi Mi::operator~() const { return P(this->v, kM - 2); }
const Mi kG = 3, kiG = ~kG;
using Poly = vector<Mi>;
int Scale(int n) {
int l = 1;
for (; l < n; l <<= 1) {
}
return l;
}
const int kL = 22;
Mi kO[kL], kiO[kL];
int _Init_omega = []() {
for (int i = 0; i < kL; ++i) {
kO[i] = P(kG, (kM - 1) / (1 << i));
kiO[i] = P(kiG, (kM - 1) / (1 << i));
}
return 0;
}();
int kBr[1 << kL];
void InitBr(int n) {
for (int i = 0; i < n; ++i) {
kBr[i] = kBr[i >> 1] >> 1 | (i & 1) * (n / 2);
}
}
int mxn;
void Init(int mx) {
mxn = Scale(mx);
InitBr(mxn);
}
加、减、多项式乘单项式
Poly operator+(const Poly &a, const Poly &b) {
Poly s(max(a.size(), b.size()));
for (int i = 0; i < s.size(); ++i) {
s[i] = (i < a.size() ? a[i] : 0) + (i < b.size() ? b[i] : 0);
}
return s;
}
Poly operator-(const Poly &a, const Poly &b) {
Poly s(max(a.size(), b.size()));
for (int i = 0; i < s.size(); ++i) {
s[i] = (i < a.size() ? a[i] : 0) - (i < b.size() ? b[i] : 0);
}
return s;
}
Poly operator*(const Poly &a, const Mi &b) {
Poly s = a;
for (Mi &v : s) {
v *= b;
}
return s;
}
多项式乘法/卷积
详见我的 深入理解 FFT。
知周所众,浮点运算
考虑在模意义下能够替代单位根的东西。
可以发现,原根的性质和单位根很像,设
void NTT(const Poly &a, Poly &b, bool iv) {
int n = a.size();
b.resize(n);
for (int i = 0; i < n; ++i) {
b[i] = a[kBr[i]];
}
for (int l = 1, c = 0; l < n; l <<= 1, ++c) {
Mi bo = (iv ? kiO : kO)[c + 1], so = 1;
for (int i = 0; i < l; ++i) {
for (int j = 0; j < n; j += l << 1) {
Mi v = so * b[j + l + i];
b[j + l + i] = b[j + i] - v;
b[j + i] += v;
}
so *= bo;
}
}
if (iv) {
Mi _v = ~Mi(n);
for (Mi &v : b) {
v *= _v;
}
}
}
void iNTT(Poly &a, bool iv) {
Poly b;
NTT(a, b, iv);
a = b;
}
Poly operator|(const Poly &x, const Poly &y) {
Poly s = x;
for (int i = 0; i < x.size(); ++i) {
s[i] *= y[i];
}
return s;
}
Poly operator*(const Poly &x, const Poly &y) {
Poly _x, _y;
NTT(x, _x, 0), NTT(y, _y, 0);
NTT(_x | _y, _x, 1);
return _x;
}
多项式求逆
我们有这样一个问题:给定
考虑对界进行倍增,边界情况为
假设我们现在已经知道了
Poly operator~(const Poly &a) {
int n = a.size();
Poly s(1, ~a[0]);
for (int l = 2; l <= n; l <<= 1) {
InitBr(l << 1);
Poly b = a;
b.resize(l), b.resize(l << 1);
s.resize(l << 1);
iNTT(s, 0), iNTT(b, 0);
for (int i = 0; i < (l << 1); ++i) {
s[i] = s[i] * (Mi(2) - b[i] * s[i]);
}
iNTT(s, 1);
s.resize(l);
}
return s;
}
求导/积分
Poly Deriv(const Poly &a) {
int n = a.size();
Poly s(n);
for (int i = 0; i < n - 1; ++i) {
s[i] = a[i + 1] * (i + 1);
}
return s;
}
Poly Integ(const Poly &a) {
int n = a.size();
Poly s(n);
for (int i = 1; i < n; ++i) {
s[i] = a[i - 1] / i;
}
return s;
}
牛顿迭代
极其重要,用来解决这种问题:
已知
假设我们已知
计算时的一些注意点:
的精度是 的。 的精度只需做到 。 的最低次项至少是 ,所以 的精度只需做到 。 为 的简写而不是 的简写。
开根
给定
发现
当只有常数项是,开放结果是二次剩余,模板题保证了
Poly Sqrt(const Poly &a) {
int n = a.size();
Poly s(1, 1);
Mi iv2 = ~Mi(2);
for (int l = 2; l <= n; l <<= 1) {
Poly b = a;
b.resize(l), b.resize(l << 1);
Poly _s = ~s;
_s.resize(l << 1);
InitBr(l << 1);
s = (s + b * _s) * iv2;
s.resize(l);
}
return s;
}
带余除法
首先直接求
但这样子得到的多项式是
考虑原式:
我们的
考虑将多项式反转。我们有
那么有:
此时就能用多项式求逆做了,求出
我们有:
因此
标签:
笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具