多项式
板子
先放一个 \(\rm NTT\) 的板子。
#include<bits/stdc++.h>
#define N 1<<?
#define P ?
using namespace std;
int qpow(int k,int b){
int ret=1;
while(b){
if(b&1)ret=1ll*ret*k%P;
k=1ll*k*k%P,b>>=1;
}
return ret;
}
int f[N],g[N],h[N];
int n,lim=1,r[N];
int gn,tp,inv;
void ntt(int *x,int lim,int opt){
for(int i=0;i<lim;i++)
if(r[i]<i)swap(x[i],x[r[i]]);
for(int m=2,k=1;m<=lim;m<<=1,k<<=1){
gn=qpow(3,(P-1)/m);
for(int i=0;i<lim;i+=m){
for(int j=0,g=1;j<k;j++,g=1ll*g*gn%P){
tp=1ll*x[i+j+k]*g%P;
x[i+j+k]=(x[i+j]-tp+P)%P;
x[i+j]=(x[i+j]+tp)%P;
}
}
}
if(opt==-1){
reverse(x+1,x+lim),inv=qpow(lim,P-2);
for(int i=0;i<lim;i++)
x[i]=1ll*x[i]*inv%P;
}
}
int main(){
//n++;
while(lim<(n<<1))lim<<=1;
for(int i=0;i<lim;i++)
r[i]=(r[i>>1]>>1)+(i&1)*(lim>>1);
ntt(f,lim,1),ntt(g,lim,1);
for(int i=0;i<lim;i++)
h[i]=1ll*f[i]*g[i]%P;
ntt(h,lim,-1);
return 0;
}
我写的 NTT 里面的 IDFT 会把数组翻转一遍,也有一种是加了一个对单位根求逆的操作。这个东西应该和 FFT 里 IDFT 的那个矩阵差不多,估计只是两种不同的实现。
P4245 【模板】任意模数多项式乘法
用一下三个数作为模数,然后 \(\text{CRT}\) 大力合并:
这三个质数的原根中都含有 \(3\),就可以写了。
有个问题是合并的时候会爆炸。
先处理前两个:
能够得到 \(ans\equiv x+k_1P_1\space(\text{mod}\space P_1P_2)\),把这个东西记为 \(t\) .
求得 \(ans\equiv t+k_4P_1P_2\space(\text{mod}\space P_1P_2P_3)\) .
细节很烦。做 \(3\) 次 \(\text{NTT}\) 记得清零。
P4238 【模板】多项式乘法逆
倍增法,求模 \(x^n\) 的逆元时,假设已求出了模 \(x^{\lceil\frac{n}{2}\rceil}\) 的逆元.
设两者分别为 \(B\) 和 \(B'\):
平方一下
乘一个 \(A\)
初始值 \(B_{1}=\{A_0^{-1}\}\) .
往下递归即可。
根据主定理得复杂度为 \(\text{T}(n)=\text{T}(n/2)+n\log n=\text{O}(n\log n)\) .
P4512 【模板】多项式除法
记 \(F_R\) 为 \(F\) 系数翻转后的函数。
那么 \(F_R(x)=F(\frac{1}{x})\cdot x^k\) .
\(f,g,q,r\) 的次数分别为 \(n,m,n-m,m-1\):
套一个求逆的板子即可算出 \(q\) .
然后
就做完了。时间复杂度 \(\text{O}(n\log n)\) .
P4723 【模板】常系数齐次线性递推
\(\text{Fibonacci}\) 数列是如何展开的?
以 \(x_5\) 为例:
\(x_5\Rightarrow x_4+x_3\Rightarrow 2x_3+x_2\)
\(\Rightarrow 3x_2+2x_1\Rightarrow 5x_1+3x_0\) .
把下标换成指数,容易发现答案是 \(x^5\) 对其特征多项式 \(x^2-x-1\) 取模的结果。
一般数列的特征多项式是 \(G(x)=x^{k}-f_0x^k-f_1x^{k-1}-\dots-f_{k-1}x^1-f_kx^0\) .
写一个快速幂求 \(F(n)=x^n\) 对 \(G(x)\) 取模的值。
时间复杂度 \(\text{O}(k\log k\log n)\) .
细节调不动了。
P4725 【模板】多项式对数函数(多项式 ln)
记 \(f(x)=\ln (x)\),则
利用链式法则,两边求导得
对 \(F(x)\) 求逆可得 \(G'(x)\).
积回去:
时间复杂度 \(O(n\log n)\).
P4726 【模板】多项式指数函数(多项式 exp)
牛顿迭代
如何得到一个函数 \(f(x)\) 的零点的近似值?
若已经有一个近似值 \(x_0\),用过 \((x_0,f(x_0))\),斜率为 \(f'(x_0)\) 的直线得到与 \(x\) 轴的交点,得到新的 \(x_0\),可以迅速逼近精确值。
切线方程
令 \(\displaystyle y=0\rightarrow x=x_0-\frac{f(x_0)}{f'(x_0)}\).
若要求 \(G(x)\) 满足 \(F(G(x))=0\),可以每次令
使其逼近真实值。
有一个结论是迭代一次后精度翻倍,不知道怎么证的。
于是每次 \(\displaystyle n\rightarrow \lceil\frac{n}{2}\rceil\) 递归进去做。
令 \(F(x)\rightarrow\ln x\),使 \(\displaystyle F(G(x))=\ln G(x)-A(x)\equiv 0\) 即可。
代入上面的式子:
每次迭代做一遍多项式求逆、求 \(\ln\) 和乘法。
时间复杂度 \(T(n)=T(\frac{n}{2})+O(n\log n)=O(n\log n)\).
P5205 【模板】多项式开根
设 \(\displaystyle H^2(x)\equiv F(x)\text{ (mod }x^{\lceil\frac{n}{2}\rceil})\).
多项式求逆即可。
由主定理得时间复杂度 \(O(n\log n)\).
P5245 【模板】多项式快速幂
由于 \(n<p\),所以 \(f(x)^p\equiv a_0=1\),可以将 \(k\) 直接对 \(p\) 取模。
一般地,若 \(f(x)\) 为 \(n\) 次多项式,\(p\) 为质数,有
归纳法:设 \(f(x)\) 为 \(k\) 阶多项式,且\(f(x)=g(x)+a_kx^k\),结论对 \(k-1\) 次多项式成立。
本题使用 \(\ln+\exp\) 即可。时间复杂度 \(O(n\log n)\).
P5273 【模板】多项式幂函数(加强版)
不保证 \(f(0)=1\).
把 \(f(x)\) 的每一项都除以 \(f(0)\):
\(f(0)=0\) 怎么办?
考虑将 \(f\) 降次后升次,具体就是找到第一个非 \(0\) 项 \(a_tx^t\):
然后使用上面的方法即可。时间复杂度 \(O(n\log n)\).
不知道我整出来什么大饼。死活过不去。
P4721 【模板】分治 FFT
\(\rm Sol1\): 容易发现
多项式求逆即可。
\(\rm Sol2\):分治,考虑当前过程中 \(f_x(x\in[l,mid])\) 对 \(f_y(y\in[mid+1,r])\) 的影响。
显然这个值是 \(f_x\cdot g_{y-x}\).
有
将 \(F\) 的 \(l\sim mid\) 项和 \(G\) 的 \(1\sim r-l+1\) 项卷起来,结果再加到 \(F\) 里。
注意默认 \(F_0=1\).
时间复杂度 \(O(n\log^2n)\).
看了代码感觉还是可以理解的。
P4239 任意模数多项式乘法逆
假设已经求出 \(G'\) 满足 \(\displaystyle F(x)\times G'(x)\equiv1\space(\operatorname{mod}x^{\lfloor\frac{n}{2}\rfloor})\).
其实和 NTT 版本的是一样的。
也就是计算 \(H=F\times G'\),取反然后在常数项 \(+2\).
有一个东西是卷积的过程中对三模数做 CRT,其他情况下对给定模数直接做即可。