多项式初等函数
本文主要介绍多项式的初等函数的定义以及在 OI 中如何求解。
注意,多项式的初等函数一般定义在模 $x^n$ 意义下,但是在 OI 中,多项式系数通常还需要另外模一下 $p$ 。
友情提醒:多项式模板题里的 $n$ 表示的含义可能有细微差异(表示次数/项数),请注意分辨。
另外,本文中的代码并没有完全清空数组(懒),因此注意多测的清空问题。
1. 多项式求逆
给定多项式 $f(x)$ ,求 $f^{-1}(x)$ ,满足
解法:
首先,易知
假设现在已经求出了 $f(x)$ 在模 $x^{\left\lceil\frac{n}{2}\right\rceil}$ 下的逆元 $f_0^{-1}(x)$ ,有
两边平方可得
两边同乘 $f(x)$ 并移项可得
递归处理即可。
时间复杂度 $T(n) = T\left(\frac{n}{2}\right) + \mathcal{O}(n \log n) = \mathcal{O}(n \log n)$ 。
完整代码详见 洛谷P4238 【模板】多项式乘法逆 题解
void Inv(int n, int *a, int *b)
{
if(n == 1) { b[0] = qpow(a[0], P - 2); return; }
Inv((n + 1) / 2, a, b);
int l = 0, limit = 1;
for(; limit <= (n - 1) * 2; l++, limit *= 2);
for(int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
for(int i = 0; i < n; i++) c[i] = a[i];
for(int i = n; i < limit; i++) c[i] = 0;
NTT(c, limit, 1); NTT(b, limit, 1);
for(int i = 0; i < limit; i++)
b[i] = (2 - c[i] * b[i] % P + P) % P * b[i] % P;
NTT(b, limit, -1);
for(int i = n; i < limit; i++) b[i] = 0;
}
2. 多项式开根
给定多项式 $g(x)$ ,求 $f(x)$ 满足
解法:
首先讨论 $[x^0]g(x)$ 不为 $0$ 的情况,易知
若 $[x^0]g(x)$ 没有平方根,则多项式 $g(x)$ 没有平方根
另外,$\left[x^0\right] g(x)$ 可能有多个平方根,因此选取不同的根会求出不同的 $f(x)$ 。
假设现在已经求出了 $g(x)$ 在模 $x^{\left\lceil\frac{n}{2}\right\rceil}$ 意义下的平方根 $f_0(x)$ ,则有
于是倍增计算即可。
时间复杂度 $T(n)=T\left(\frac{n}{2}\right)+\mathcal{O}(n \log n)=\mathcal{O}(n \log n)$
完整代码详见 洛谷P5205 【模板】多项式开根 题解
void Sqrt(int n, int *a, int *b)
{
if(n == 1) { b[0] = 1; return; }
Sqrt((n + 1) / 2, a, b);
int l = 0, limit = 1;
for(; limit <= (n - 1) * 2; l++, limit *= 2);
for(int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1 | ((i & 1) << (l - 1)));
for(int i = 0; i < limit; i++) d[i] = 0;
Inv(limit / 2, b, d);
for(int i = 0; i < n; i++) c[i] = a[i];
for(int i = n; i < limit; i++) c[i] = 0;
NTT(c, limit, 1); NTT(d, limit, 1);
for(int i = 0; i < limit; i++) c[i] = c[i] * d[i] % P;
NTT(c, limit, -1);
static int inv2 = qpow(2, P - 2);
for(int i = 0; i < n; i++) b[i] = (b[i] + c[i]) % P * inv2 % P;
for(int i = n; i < limit; i++) b[i] = 0;
}
当 $[x^0]g(x) \ne 1$ 时,需要用二次剩余来计算 $[x^0]f(x)$ ,详见:[链接] 咕咕咕咕咕咕。。。。
两个模板题的区别在于,前者保证 $a_0=1$ ,后者不保证 $a_0=1$ 但是保证 $a_0$ 为模 $p$ 意义下的二次剩余。
3. 多项式除法 & 带余除法
给定多项式 $f(x),g(x)$ ,求 $g(x)$ 除 $f(x)$ 的商 $Q(x) $和余数 $R(x)$ 。
解法:
考虑构造变换
观察可知其实质为反转 $f(x)$ 的系数。
设 $n=\operatorname{deg} f,~m = \operatorname{deg} g$ (多项式的次数)
将 $f(x)=Q(x) g(x)+R(x)$ 中的 $x$ 替换成 $\frac{1}{x}$ 并将其两边都乘上 $x^n$ ,得到
注意到上式中 $R^R(x)$ 的 $x^{n-m+1}$ ,则将其放到模 $x^{n-m+1}$ 意义下即可消除 $R^R(x)$ 带来的影响.
又因为 $Q^R(x)$ 的次数为 $(n - m) < (n - m + 1)$ ,故 $Q^R(x)$ 不会受到影响,则
使用多项式求逆即可求出 $Q(x)$ ,将其反代回去即可得到 $R(x)$ 。
时间复杂度 $\mathcal{O}(n \log n)$
完整代码详见 洛谷P4512 【模板】多项式除法 题解
void Div(int n, int m, int *a, int *b, int *ans1, int *ans2)
{
for(int i = 0; i < m; i++) c[i] = b[i];
reverse(c, c + m); Inv(n, c, d);
int l = 0, limit = 1;
for(; limit <= (n - 1) * 2; l++, limit *= 2);
for(int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
for(int i = 0; i < n; i++) c[i] = a[i];
for(int i = n; i < limit; i++) c[i] = 0;
reverse(c, c + n);
NTT(c, limit, 1); NTT(d, limit, 1);
for(int i = 0; i < limit; i++) ans1[i] = c[i] * d[i] % P;
NTT(ans1, limit, -1);
reverse(ans1, ans1 + n - m + 1);
for(int i = 0; i < n - m + 1; i++) c[i] = ans1[i];
for(int i = n - m + 1; i < limit; i++) c[i] = 0;
for(int i = 0; i < m; i++) d[i] = b[i];
for(int i = m; i < limit; i++) d[i] = 0;
NTT(c, limit, 1); NTT(d, limit, 1);
for(int i = 0; i < limit; i++) c[i] = c[i] * d[i] % P;
NTT(c, limit, -1);
for(int i = 0; i < m - 1; i++) ans2[i] = (a[i] + P - c[i]) % P;
}
4. 多项式对数函数 & 指数函数
多项式对数函数
给定整系数多项式 $f(x)$ ,求 $g(x)$ 满足
在说明解法之前,我们先证明一个定理。
定理:当且仅当 $[x^0]f(x) = 1$ 时,$f(x)$ 有对数多项式。
证明:
先证明一个引理。
引理:当 $\forall x\in \mathbb{Q} ,~x \ne 1$ 有 $\ln x \not\in \mathbb{Q}$ ($\mathbb{Q}$ 为有理数集)。
证明:考虑反证法。若 $c=\ln x\in \mathbb{Q}$ ,则 $x = \mathrm{e}^c$ ,显然有 $c \ne 0$ 。
因为 $\mathrm{e}$ 为超越数,所以 $\mathrm{e}^{c}$ 对于 $\forall c \in \mathbb{Q}$ 都有 $\mathrm{e}^c\not\in \mathbb{Q}$ ,即 $x \not\in \mathbb{Q}$ ,与假设矛盾。$\square$
考虑用先求导再积分的方式获得其对数
当然这个 $f(x)$ 显然已经确定了它的构成,那么我们可以写成
其中 $C=\ln f(0)$ 为积分常数。
根据引理,又因为无理数在模意义下无意义,所以 $f(0) = 1$ 即 $[x^0]f(x)=1$ 。
对于除 $[x^0]f(x)$ 外的项,显然其导数和积分都为有理数。证毕。
解法:
其实我们刚刚已经推导出了
这里不熟悉微积分的同学可能没法一眼看出来,那么给个公式提示一下
因此实际上我们可以 $\mathcal{O}(n)$ 求导和积分,加上求逆的 $\mathcal{O}(n \log n)$ ,总时间复杂度为 $\mathcal{O}(n \log n)$
完整代码详见 洛谷P4725 【模板】多项式对数函数(多项式 ln) 题解
void Diff(int n, int *a, int *b) // 求导
{
b[n - 1] = 0;
for(int i = 1; i < n; i++) b[i - 1] = i * a[i] % P;
}
void Int(int n, int *a, int *b) // 积分
{
b[0] = 0;
for(int i = 1; i < n; i++) b[i] = a[i - 1] * qpow(i, P - 2) % P;
}
void Ln(int n, int *a, int *b)
{
Diff(n, a, c); Inv(n, a, d);
Mul(n, n, c, d, t); Int(n, t, b); // Mul 是乘法
}
多项式指数函数
给定整系数多项式 $f(x)$ ,求 $g(x)$ 满足
定理:当且仅当 $[x^0]f(x) = 0$ 时,$f(x)$ 有指数多项式,否则常数项不收敛。
证明:不知道。
但是你想想,是吧,对吧,啊,所以它是对的。
解法:
考虑对 $\exp f(x)$ 求导
比较两边系数可得
考虑分治 FFT ,时间复杂度为 $\mathcal{O}(n \log^2 n)$ 。(听说常数很小?)
代码详见 [链接] 咕咕咕。。。。
当然,用牛顿迭代法可以达到 $\mathcal{O}(n \log n)$ ,本文不打算讲。
5. 多项式三角函数
给定多项式 $f(x)$ ,求模 $x^n$ 意义下的 $\sin f(x),\cos f(x), \tan f(x)$
解法:
由 $\mathrm{e}^{\mathrm{i} x}=\cos x+\mathrm{i} \sin x$ 可得
代入 $f(x)$ ,有
这里因为通常在 $\mathbb{Z}_{998244353}$ 上做 NTT ,因此
时间复杂度 $\mathcal{O}(n \log^2 n)$ 或 $\mathcal{O}(n \log n)$
代码详见 [链接] 咕咕咕。。。。
6. 多项式反三角函数
给定多项式 $f(x)$ ,求模 $x^n$ 意义下的 $\arcsin f(x),\arccos f(x), \arctan f(x)$
解法:
对反三角函数求导再积分可得
代入 $f(x)$ 有
时间复杂度 $\mathcal{O}(n \log n)$
代码详见 [链接] 咕咕咕。。。。
参考文献: