多项式全家桶
以下若无特殊说明,函数均为多项式函数。系数对 998244353 取模。
多项式乘法#
多项式牛顿迭代#
设有任意可泰勒展开的函数 ,求满足 的多项式 ,此时可以使用牛顿迭代法求解。
具体的讲,牛顿迭代法是有以下过程:
-
首先求出 时的 。
-
假设我们已经求出了 的解 ,则 的解为
证:
我们直接在 处对 泰勒展开:考虑到 ,我们有 。
因此又有:
时间复杂度:,注意递归时 。
关于求导:将 看作一次自变量, 看作常数即可,由于待求值为 而不是 因此本质应为求偏导(或形式幂级数)一类的方法,这里不做深究。
多项式求逆#
求满足 的 。
由题意,,考虑使用牛顿迭代,构造函数 ,即求解 。
-
当 时,(显而易见的, 时不存在逆元)。
-
当 时,
多项式 ln#
求满足 的 。
考虑求导再积分:
取 ,可以求出 。
由于 是超越数,因此 。
因此 。
多项式 exp#
求满足 的 。
由于 ,使用牛顿迭代法构造 。
首先由上面得 ,考虑若当前已经求出
多项式幂函数#
求满足 的 。
首先不妨假设 ,对于其他取值可以通过左右移位、乘系数等变换来将常数项变成 1。
利用对数函数的性质:
多项式三角函数#
中学阶段学过的:
使用多项式 exp 直接代入即可。(注意 下 的取值)。
多项式开方#
求满足 的 。
首先假设你已经通过一些方法求出了 时的解(如二次剩余、BSGS)。
之后牛顿迭代,过程读者自证不难。
多项式除法&取余#
设有 项式 , 项式 ,求 的商 和余式 。
考虑到若 ,则 ,又因为 的次数有特殊性,所以尝试将 利用模运算消掉。
定义 表示将 的 系数翻转,忽略 表示全部翻转,则由多项式乘法的定义不难看出 。
则我们有
发现 最高次为 ,而 非零最低次至少为 ,因此直接 ,即变成
其中 取模不受影响,使用多项式求逆即可算出 。
板子速查#
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, MOD = 998244353, G = 3, Gi = 332748118;
namespace Cipolla { // 二次剩余
int n, p, oyds;
struct comp {
int r, i;
comp(int _r = 0, int _i = 0) { r = _r, i = _i; }
comp operator*(const comp &rhs) const {
return comp((r * rhs.r % p + i * rhs.i % p * oyds) % p,
(r * rhs.i % p + i * rhs.r) % p);
}
};
int qpow(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = a * a % p)
if (b & 1) res = res * a % p;
return res;
}
comp cpow(comp a, int b) {
comp res = comp(1, 0);
for (; b; b >>= 1, a = a * a)
if (b & 1) res = res * a;
return res;
}
int check(int x) {
if (x % p == 0) return 0;
return qpow(x, (p - 1) >> 1);
}
int work() {
if (check(n) == p - 1) return -1;
int a;
do {
a = rand() % p;
oyds = (a * a % p - n + p) % p;
} while (check(oyds) != p - 1);
return cpow(comp(a, 1), p + 1 >> 1).r % p;
}
int Cip(int _n, int _p) {
n = _n, p = _p;
n %= p;
if (n == 0) {
return 0;
}
int x1 = (work() + p) % p, x2 = p - x1;
return min(x1, x2);
}
} // namespace Cipolla
int inv(int a, int b = MOD - 2, int p = MOD) { // 快速幂
int res = 1;
for (; b; b >>= 1, a = a * a % p)
if (b & 1) res = res * a % p;
return res % p;
}
int init(int n) { // 预处理
int len = 1;
while (len < n) len <<= 1;
return len << 1;
}
int rev[N];
void initrev(int len) {
for (int i = 0; i < len; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (len >> 1));
}
void ntt(int *a, int n, int tp) { // 快速数论变换
for (int i = 0; i < n; i++)
if (i < rev[i]) swap(a[i], a[rev[i]]);
for (int block = 1; block < n; block <<= 1) {
int siz = block << 1;
int g1 = inv((tp == 1) ? G : Gi, (MOD - 1) / siz);
for (int l = 0, r, gn; l < n; l += siz) {
gn = 1, r = l + block - 1;
for (int i = l; i <= r; i++, gn = gn * g1 % MOD) {
int tmp = a[i];
a[i] = (tmp + gn * a[i + block] % MOD + MOD) % MOD;
a[i + block] = (tmp - gn * a[i + block] % MOD + MOD) % MOD;
}
}
}
if (tp == 1) return;
int orz = inv(n);
for (int i = 0; i < n; i++) a[i] = (a[i] * orz + MOD) % MOD;
}
void polymul(int *f, int *g, int n, int m, int *tar) { // 多项式乘法
int len = init(n + m - 1) >> 1;
initrev(len);
static int a[N], b[N];
for (int i = 0; i < n; i++) a[i] = f[i];
for (int i = 0; i < m; i++) b[i] = g[i];
for (int i = n; i < len; i++) a[i] = 0;
for (int i = m; i < len; i++) b[i] = 0;
ntt(a, len, 1), ntt(b, len, 1);
for (int i = 0; i < len; i++) tar[i] = a[i] * b[i] % MOD;
ntt(tar, len, -1);
for (int i = n + m - 1; i < len; i++) tar[i] = 0;
}
void polyinv(int n, int *f, int *g) { // 多项式乘法逆
static int tmp[N];
if (n == 1) {
g[0] = inv(f[0]);
return;
}
polyinv((n + 1) >> 1, f, g);
polymul(f, g, n, n, tmp), polymul(tmp, g, n, n, tmp);
for (int i = 0; i < n; i++) g[i] = (2ll * g[i] - tmp[i] + MOD) % MOD;
}
void polydiv(int *f, int *g, int n, int m, int *q, int *r) { // 多项式除法 and 取余
static int a[N], b[N], invb[N];
for (int i = 0; i < n; i++) a[i] = f[i];
for (int i = 0; i < m; i++) b[i] = g[i];
reverse(a, a + n), reverse(b, b + m);
int siz = n - m + 1;
polyinv(siz, b, invb);
polymul(a, invb, n, siz, q);
reverse(q, q + siz);
int len = init(n);
for (int i = siz; i < 2ll * len; i++) q[i] = 0;
polymul(g, q, m, siz, r);
for (int i = 0; i < m; i++) r[i] = (f[i] - r[i] + MOD) % MOD;
}
void polyderi(int *f, int n, int *g) { // 多项式求导
for (int i = 1; i < n; i++) g[i - 1] = i * f[i] % MOD;
g[n - 1] = 0;
}
void polyinte(int *f, int n, int *g) { // 多项式不定积分
for (int i = 1; i < n; i++) g[i] = f[i - 1] * inv(i) % MOD;
g[0] = 0;
}
void polyln(int *f, int n, int *g) { // 多项式 ln
static int a[N], b[N], c[N];
polyderi(f, n, a), polyinv(n, f, b);
polymul(a, b, n, n, c);
polyinte(c, n, g);
for (int i = n; i < n * 2; i++) g[i] = 0;
}
void polyexp(int *f, int n, int *g) { // 多项式 exp
static int tmp[N];
if (n == 1) {
g[0] = 1;
return;
}
polyexp(f, (n + 1) >> 1, g);
polyln(g, n, tmp);
for (int i = 0; i < n; i++) tmp[i] = (f[i] - tmp[i] + MOD) % MOD;
tmp[0]++;
polymul(g, tmp, n, n, g);
for (int i = n; i < n * 2; i++) g[i] = 0;
}
void polypow(int *f, int n, char *k, int *g) { // 多项式快速幂
if (k[0] == '0') {
g[0] = 1;
for (int i = 1; i < n; i++) g[i] = 0;
return;
}
int fst = n, k1 = 0, k2 = 0, k3 = 0, lenk = strlen(k); // 处理巨大恶臭指数
for (int i = 0; i < lenk; i++) {
int num = k[i] - '0';
k1 = (k1 * 10 % MOD + num) % MOD;
k2 = (k2 * 10 % (MOD - 1) + num) % (MOD - 1);
if (k3 < n) k3 = k3 * 10 + k[i] - '0';
}
if (f[0] == 0 && k3 > n) {
for (int i = 0; i < n; i++) g[i] = 0;
return;
}
static int tmp[N], a[N];
for (int i = 0; i < n; i++)
if (f[i]) {
fst = i;
break;
}
if (k1 * fst >= n) {
for (int i = 0; i < n; i++) g[i] = 0;
return;
}
int d = f[fst], invd = inv(d), pd = inv(d, k2);
for (int i = fst; i < n; i++) a[i - fst] = f[i] * invd % MOD;
for (int i = n - fst; i < n; i++) a[i] = 0;
polyln(a, n - fst, tmp);
for (int i = 0; i < n - fst; i++) tmp[i] = tmp[i] * k1 % MOD;
polyexp(tmp, n - fst, g + fst * k1);
for (int i = 0; i < fst * k1; i++) g[i] = 0;
for (int i = fst * k1; i < n * 2; i++) g[i] = g[i] * pd % MOD;
}
void polysqrt(int *f, int n, int *g) { // 多项式开根
if (n == 1) {
g[0] = Cipolla::Cip(f[0], MOD);
return;
}
polysqrt(f, n + 1 >> 1, g);
static int tmp[N], inv2 = inv(2);
polyinv(n, g, tmp);
polymul(f, tmp, n, n, tmp);
for (int i = 0; i < n; i++) g[i] = (g[i] + tmp[i]) % MOD * inv2 % MOD;
for (int i = n; i < n * 2; i++) g[i] = 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现