多项式全家桶(还有些不会的)

多项式全家桶

记号约定

  • \(f^{(i)}(x)\):对 \(f(x)\)\(i\) 阶导。
  • \([x^i]f(x)\)\(f(x)\)\(i\) 次项系数。

多项式微积分

我们知道:

\[\begin{aligned} \dfrac{\textrm d}{\textrm dx}x^n&=nx^{n-1}\\ \int x^n\textrm dx&=\dfrac{x^{n+1}}{n+1} \end{aligned} \]

以及可加性,于是容易写出代码:

#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;
}

时间复杂度 \(\mathcal O(n)\)

多项式加减法

对应项相加减即可,于是我都没封装。

时间复杂度 \(\mathcal O(n)\)

多项式乘法

前置知识: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 的 \(T(n)=2T(\frac{n}{2})+\mathcal O(n)=\mathcal O(n\log n)\)

任意模数多项式乘法

还不会。

多项式牛顿迭代

前置知识:牛顿迭代、泰勒展开。请先自行学习。

多项式牛顿迭代并不会被封装,但它是一个十分重要的推导方法,下面都会用到。

多项式牛顿迭代用于解决这样的问题:给定多项式 \(g(x)\),求多项式 \(f(x)\) 使得 \(g(f(x))\equiv 0\pmod{x^n}\)

首先,\([x^0]g(f(x))\equiv 0\) 也就是 \(g(f(x))\equiv 0\pmod{x}\) 的解需要单独求出。

然后设 \(f_0\)\(g(f(x))\equiv 0\pmod{x^{\left\lceil\frac{n}{2}\right\rceil}}\) 的解。

\(g(f(x))\)\(f_0(x)\) 处进行泰勒展开:

\[\sum\limits_{i=0}^{+\infty}\dfrac{g^{(i)}(f_0(x))}{i!}(f(x)-f_0(x))^i\equiv 0\pmod{x^n} \]

因为 \(f(x)-f_0(x)\) 的最低非零项次数最低为 \(\left\lceil\frac{n}{2}\right\rceil\),所以 \(\forall i\ge 2:(f(x)-f_0(x))^i\equiv 0\pmod{x^n}\),进一步得到:

\[\begin{aligned} g(f_0(x))+g'(f_0(x))(f(x)-f_0(x))&\equiv 0&\pmod{x^n}\\ f(x)&\equiv f_0(x)-\dfrac{g(f_0(x))}{g'(f_0(x))}&\pmod{x^n} \end{aligned} \]

多项式乘法逆元

设我们要求给定函数 \(h(x)\) 的乘法逆元,则有方程:

\[g(f(x))=\dfrac{1}{f(x)}-h(x)\equiv 0\pmod{x^n} \]

应用多项式牛顿迭代可得:

\[\begin{aligned} f(x)&\equiv f_0(x)-\dfrac{\frac{1}{f_0(x)}-h(x)}{-\frac{1}{f_0^2(x)}}&\pmod{x^n}\\ &\equiv 2f_0(x)-f_0^2(x)h(x)&\pmod{x^n} \end{aligned} \]

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;
}

时间复杂度为 \(T(n)=T(\frac{n}{2})+\mathcal O(n\log n)=\mathcal O(n\log n)\)

多项式开根

设我们要求 \(f(x)\) 满足 \(f^2(x)\equiv h(x)\pmod{x^n}\)

首先在 \(n=1\) 时需要单独求出。如果题目保证了 \([x^0]h(x)=1\),则可以直接 \([x^0]f(x)=1\),否则需要使用 二次剩余

有方程:

\[g(f(x))=f^2(x)-h(x)\equiv 0\pmod{x^n} \]

应用多项式牛顿迭代可得:

\[\begin{aligned} f(x)&\equiv f_0(x)-\dfrac{f_0^2(x)-h(x)}{2f_0(x)}&\pmod{x^n}\\ &\equiv\dfrac{f_0^2(x)+h(x)}{2f_0(x)}&\pmod{x^n} \end{aligned} \]

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;
}

时间复杂度为 \(T(n)=T(\frac{n}{2})+\mathcal O(n\log n)=\mathcal O(n\log n)\)

多项式对数函数

对于多项式 \(h(x)\),若 \(\ln h(x)\) 存在,则 \([x^0]h(x)=1\)

遇事不决先求导:

\[\dfrac{\textrm d\ln h(x)}{\textrm dx}\equiv\dfrac{h'(x)}{h(x)}\pmod{x^n} \]

然后两边积分:

\[\ln h(x)\equiv\int\dfrac{h'(x)}{h(x)}\textrm dx\pmod{x^n} \]

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);
}

时间复杂度为 \(\mathcal O(n\log n)\)

多项式指数函数

对于多项式 \(h(x)\),若 \(\exp h(x)\) 存在,则 \([x^0]h(x)=0\),否则 \(\exp h(x)\) 的常数项不收敛。

有方程:

\[g(f(x))=\ln f(x)-h(x)\equiv 0\pmod{x^n} \]

应用多项式牛顿迭代可得:

\[\begin{aligned} f(x)&\equiv f_0(x)-\dfrac{\ln f_0(x)-h(x)}{\frac{1}{f_0(x)}}&\pmod{x^n}\\ &\equiv f_0(x)(1-\ln f_0(x)+h(x))&\pmod{x^n} \end{aligned} \]

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);
}

时间复杂度为 \(T(n)=T(\frac{n}{2})+\mathcal O(n\log n)=\mathcal O(n\log n)\)

多项式带余除法

已知 \(f(x),g(x)\),求 \(f(x)\) 除以 \(g(x)\) 得到的商 \(Q(x)\) 和余数 \(R(x)\)

如果可以消除余数 \(R(x)\) 的影响,则多项式求逆直接就解决了。

构造变换:

\[f^R(x)=x^{\deg f}f\left(\dfrac{1}{x}\right) \]

也就是翻转 \(f(x)\) 的各项系数。

我们记 \(n=\deg f,m=\deg g\)

\(f(x)=g(x)Q(x)+R(x)\) 中的 \(x\) 替换为 \(\frac{1}{x}\),并把两边乘以 \(x^n\),得到:

\[\begin{aligned} x^nf\left(\dfrac{1}{x}\right)&=x^mg\left(\dfrac{1}{x}\right)x^{n-m}Q\left(\dfrac{1}{x}\right)+x^{n-m+1}x^{m-1}R\left(\dfrac{1}{x}\right)\\ f^R(x)&=g^R(x)Q^R(x)+x^{n-m+1}R^R(x) \end{aligned} \]

则:

\[f^R(x)\equiv g^R(x)Q^R(x)\pmod{x^{n-m+1}} \]

使用多项式求逆可以得到 \(Q(x)\),代回原式可得 \(R(x)\)

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;
		}
	}
}

时间复杂度为 \(\mathcal O(n\log n)\)

多项式幂函数

普通的多项式快速幂计算 \(f^k(x)\) 的复杂度为 \(\mathcal O(n\log n\log k)\)

\([x^0]f(x)=1\) 时,有:

\[f^k(x)=\exp(k\ln f(x)) \]

\([x^0]f(x)\ne 1\) 时,我们想到除以 \([x^0]f(x)\)\([x^0]f(x)\) 变成 \(1\)。不过当 \([x^0]f(x)=0\) 时会出问题,我们需要找到最低非零项 \(f_tx^t\),则:

\[f^k(x)=f_t^kx^{tk}\exp\left(k\ln\dfrac{f(x)}{f_tx^t}\right) \]

先放一个前者的代码,后者还没写:

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);
}

时间复杂度为 \(\mathcal O(n\log n)\)

多项式三角函数

由欧拉公式 \(\textrm e^{\textrm ix}=\cos x+\textrm i\sin x\),我们知道:

\[\begin{aligned} \sin x&=\dfrac{\textrm e^{\textrm ix}-\textrm e^{-\textrm ix}}{2\textrm i}\\ \cos x&=\dfrac{\textrm e^{\textrm ix}+\textrm e^{-\textrm ix}}{2} \end{aligned} \]

代入 \(f(x)\) 得:

\[\begin{aligned} \sin f(x)&=\dfrac{\exp(\textrm if(x))-\exp(-\textrm if(x))}{2\textrm i}\\ \cos f(x)&=\dfrac{\exp(\textrm if(x))+\exp(-\textrm if(x))}{2} \end{aligned} \]

然后 \(\tan f(x)=\frac{\sin f(x)}{\cos f(x)}\) 可以得到正切函数。

那这里的 \(\textrm i\) 是什么?由定义 \(\textrm i\equiv\sqrt{-1}\equiv\sqrt{998244352}\pmod{998244353}\),令 \(\textrm i\)\(998244352\) 模意义开根的结果即可,得到 \(\textrm i=86583718\) 或者 \(\textrm i=911660635\)

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;
	}
}

时间复杂度为 \(\mathcal O(n\log n)\)

多项式反三角函数

遇事不决先求导:

\[\begin{aligned} \frac{\textrm{d}}{\textrm{d} x} \arcsin{x} &= \frac{1}{\sqrt{1 - x^{2}}} \\ \arcsin{x} &= \int \frac{1}{\sqrt{1 - x^{2}}} \textrm{d} x \\ \frac{\textrm{d}}{\textrm{d} x} \arccos{x} &= - \frac{1}{\sqrt{1 - x^{2}}} \\ \arccos{x} &= - \int \frac{1}{\sqrt{1 - x^{2}}} \textrm{d} x \\ \frac{\textrm{d}}{\textrm{d} x} \arctan{x} &= \frac{1}{1 + x^{2}} \\ \arctan{x} &= \int \frac{1}{1 + x^{2}} \textrm{d} x \end{aligned} \]

代入 \(f\left(x\right)\) 得:

\[\begin{aligned} \frac{\textrm{d}}{\textrm{d} x} \arcsin{f\left(x\right)} &= \frac{f'\left(x\right)}{\sqrt{1 - f^{2}\left(x\right)}} \\ \arcsin{f\left(x\right)} &= \int \frac{f'\left(x\right)}{\sqrt{1 - f^{2}\left(x\right)}} \textrm{d} x \\ \frac{\textrm{d}}{\textrm{d} x} \arccos{f\left(x\right)} &= - \frac{f'\left(x\right)}{\sqrt{1 - f^{2}\left(x\right)}} \\ \arccos{f\left(x\right)} &= - \int \frac{f'\left(x\right)}{\sqrt{1 - f^{2}\left(x\right)}} \textrm{d} x \\ \frac{\textrm{d}}{\textrm{d} x} \arctan{f\left(x\right)} &= \frac{f'\left(x\right)}{1 + f^{2}\left(x\right)} \\ \arctan{f\left(x\right)} &= \int \frac{f'\left(x\right)}{1 + f^{2}\left(x\right)} \textrm{d} x \end{aligned} \]

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);
	}
}

时间复杂度为 \(\mathcal O(n\log n)\)

多项式多点求值

还不会。

多项式快速插值

还不会。

posted @ 2022-07-12 22:59  rui_er  阅读(100)  评论(1编辑  收藏  举报