多项式
拉格朗日插值
基本流程
对于一个 \(n\) 次函数 \(f(x)\),给出 \(n+1\) 个点值 \((x_i,y_i)\),保证 \(x_i\) 互不相同,求 \(f(k)\)。
考虑 crt 的思想,对于第 \(i\) 个点值 \((x_i,y_i)\),构造函数 \(f_i(x)\) 满足 \(f_i(x_i)=y_i\),且对于 \(j\ne i\),\(f_i(x_j)=0\)。
由
可以推出 \(f_i(x)\) 的表达式
所以 \(f(x)\) 的表达式即为所有 \(f_i(x)\) 的和
直接计算时间复杂度为 \(O(n^2)\),用分治 NTT 可以优化到 \(O(n\log^2 n)\)。
特殊情况
如果 \(x_i=i\),则 \(f(x)\) 可以变为
其中 \(p_i=\prod\limits_{j=0}^{i-1}(x-j)\),\(s_i=\prod\limits_{j=i+1}^{n}(x-j)\),通过预处理可以 \(O(n)\) 得到,再预处理出阶乘及逆元,整体时间复杂度就是 \(O(n)\)。
动态加点
因为
令 \(g(x)=\prod\limits_{i=0}^n(x-x_i)\),\(h(i)=\prod\limits_{j=0,j\ne i}^n\frac{y_i}{x_i-x_j}\),所以有
每次加点 \((x_{n+1},y_{n+1})\) 时对于 \(0\le i\le n\),\(h(i)\) 都会乘以 \(\frac{1}{x_i-x_{n+1}}\),然后 \(h(n+1)\) 直接算就行了,所以查询和修改操作都是 \(O(n)\) 的。
例题
[NOIP2020] 微信步数
先把无解判掉,即 \(n\) 步后每一维位移量为 \(0\) 且偏差量不超过 \(w_i\)。
转换问题,即枚举走的步数,统计有多少种方案不会走出去。进一步发现对于每一维,可供选择的位置是一段区间 \([l_i,r_i]\),所以方案数即 \(\prod(r_i-l_i+1)\)。可以通过统计位移量 \(s_i\) 的极值来计算。时间复杂度最大是 \(O(nkV)\)。
考虑优化,令 \(l_{i,j}\) 为第一轮经过 \(i\) 步第 \(j\) 维位移量的最小值,\(r_{i,j}\) 同理,\(d_j\) 表示第 \(j\) 维的 \(n\) 步后的位移量。相当对于 \([w_j-r_{i,j},w_j]\) 和 \([1,-l_{i,j}]\) 这两段区间都不能选。对于第二轮,那么 \(l'_{i,j}\) 要么就是 \(l_{i,j}\),要么就是 \(l_{i,j}+d_j\),\(r_{i,j}\) 同理。发现每一轮位移量是相同的,那么对于经过 \(i\) 步,每一轮减少的方案数也是相同的,设 \(p_{i,j}=r'_{i,j}-r_{i,j}+l_{i,j}-l'_{i,j}\) 即经过 \(i\) 步第 \(j\) 维减少的方案数,\(g_j\) 表示第一轮后第 \(j\) 维的方案数。所以我们可以将统计答案改写成以下式子:
因为第 \(x+2\) 轮走了 \(i\) 步,第 \(2\sim x+1\) 轮每一轮第 \(j\) 维会减少 \(p_{n,j}\) 种方案,所以就是第 \(x+2\) 轮经过 \(i\) 步后的方案数。容易发现第二维上限是可以算出的,后面又是关于 \(x\) 的 \(k\) 次多项式,所以可以在 \(k^2\) 的时间复杂度求出多项式系数,然后交换求和顺序变成求自然数幂和,即
补充知识
复数
定义复数域 \(\mathbb{C}\),对于 \(z\in \mathbb{C}\),\(z\) 可以唯一表示为 \(a+b\mathrm{i}\),其中 \(a,b\in \R\),定义 \(\mathrm{i}^2=-1\)。
基本运算法则:
- \(z_1+z_2=(a_1+a_2)+(b_1+b_2)\mathrm{i}\)。
- \(z_1z_2=(a_1a_2-b_1b_2)+(a_1b_2+a_2b_1)\mathrm{i}\)
- 如果 \(z_1=a+b\mathrm{i}\),\(z_2=a-b\mathrm{i}\),则 \(z_1\) 和 \(z_2\) 互为共轭。
建立复平面,实轴为 \(x\),虚轴为 \(y\),则任意 \(z\) 在复平面上都可以表示成一个点坐标 \(Z(a,b)\)。
考虑用向量表示 \(z\),即 \((\theta,r)\),其中 \(r=\lvert z\rvert=\sqrt{a^2+b^2}\),又称为 \(z\) 的模长。\(\theta\) 为 \(\overrightarrow{OZ}\) 与 \(x\) 正半轴的夹角。
简单推导可以得到 \(z=(\theta,r)=r(\cos\theta+\mathrm{i}\sin\theta)\),由欧拉公式 \(e^{\mathrm{i}\theta}=\cos \theta+\mathrm{i}\sin \theta\),可以得到 \(z=r\cdot e^{\mathrm{i}\theta}\)。
于是可得:
- \(z^n=r^n\cdot e^{\mathrm{i}n\theta}\)。
- \(\sqrt[n]{z}=\sqrt[n]{r}\cdot e^{\frac{\mathrm{i}(\theta+2k\pi)}{n}}\),其中 \(0\le k<n\)。
单位根
定义 \(\omega_n\) 为 \(n\) 次单位根,即 \(\omega_n^{n}=1\)。
所以 \(\omega_n^n=1=e^{\mathrm{i}\cdot 0}\),于是 \(\omega_n=e^{\frac{\mathrm{i}2k\pi}{n}}\),为了方便,令 \(\omega_{n}=e^{\frac{\mathrm{i}2\pi}{n}}=\cos(\frac{2\pi}{n})+\mathrm{i}\sin(\frac{2\pi}{n})\)。
那么 \(n\) 个根可以表示为 \(\omega^{0}_{n},\omega^{1}_{n},\cdots,\omega^{n-1}_{n}\)。
对数
\(a^c=b\Longleftrightarrow \log_ab=c\)
\(\ln a=\log_ea\)
\(\log_{a}(bc)=\log_ab+\log_ac\)
\(\log_{a}(\frac{b}{c})=\log_ab-\log_ac\)
\(\log_{a}(b^n)=n\log_ab\)
\(\log_{a}b=\frac{\log_cb}{\log_ca}\)
极限
定义
设 \(f(x)\) 在 \(x_0\) 的邻域有定义,若对于 \(\forall \varepsilon >0\),\(\exist \delta>0\),使得当 \(0<|x-x_0|<\delta\) 时,有 \(|f(x)-a|<\varepsilon\),则记 \(a=\lim\limits_{x\to x_0}f(x)\)。
运算法则
若 \(\lim\limits_{x\to x_0}f(x)=a\),\(\lim\limits_{x\to x_0}g(x)=b\),则
- \(\lim\limits_{x\to x_0}(f+g)(x)=a+b\)
- \(\lim\limits_{x\to x_0}(fg)(x)=ab\)
- 如果 \(b\ne 0\),\(\lim\limits_{x\to x_0}(\frac{f}{g})=\frac{a}{b}\)
如果 \(g(x)\) 在 \(u\) 处有极限 \(a\),\(f(x)\) 在 \(a\) 处有极限 \(b\),且存在 \((u-\delta,u)\cup (u,u+\delta)\) 使 \(g(x)\ne a\)。则
- \(\lim[c\cdot f(x)]=c\cdot \lim f(x)\)
- \(\lim[f(x)]^n=[\lim f(x)]^n\)
夹逼定理
设 \(g(x)\le f(x)\le h(x)\),且 \(\lim\limits_{x\to x_0}g(x)=\lim\limits_{x\to x_0}h(x)=a\),则 \(\lim\limits_{x\to x_0}f(x)=a\)。
可证得重要结论
单调有界准则
设函数 \(f(x)\) 在 \(x_0\) 的某左邻域单调且有界,则其左极限 \(\lim_{x\to x_0^{-}}f(x)\) 必定存在。
类似还有右极限和趋于无穷的单调有界准则。
可证得重要结论
存在。
导数
定义
设函数 \(f(x)\) 在 \(x_0\) 的某邻域 \(U(x_0)\) 内有定义,若极限
存在,则称 \(f(x)\) 在 \(x_0\) 处可导,该极限为 \(f(x)\) 在 \(x_0\) 处的导数,记为 \(f'(x_0)\)。也可改写为
如果 \(f(x)\) 在区间 \(I\) 上处处可导,定义 \(f(x)\) 在区间上的导函数 \(f'(x)\) 为
如果 \(y=f(x)\),那么 \(f'(x)=y'=\frac{dy}{dx}=\frac{d}{dx}f(x)\)。
\(f(x)\) 的 \(n\) 阶导记作 \(f^{(n)}(x)\)。
运算法则
若函数 \(f(x)\) 和 \(g(x)\) 可导,则
- \((f+g)'=f'+g'\)
- \((fg)'=f'g+fg'\)
- 如果在 \(g\ne 0\) 处,\((\frac{f}{g})'=\frac{f'g-fg'}{g^2}\)
链式法则:若 \(u=g(x)\) 可导,\(v=f(u)\) 可导,则复合函数 \(f\circ g\) 可导,且 \((f\circ g)'(x)=f'(u)g'(x)=f'(g(x))g'(x)\)。
指数对数化:指数和底数都含 \(x\) 的函数叫幂指函数,可通过指数对数化,\(f(x)^{g(x)}=e^{\ln f(x)^{g(x)}}=e^{g(x)\ln f(x)}\),然后通过链式法则和乘法法则求导。
常用结论
-
\(\frac{d}{dx}x^{n}=nx^{n-1}\),用定义加二项式定理可证明。
-
\(\frac{d}{dx}a^x=a^x\ln a\)。
-
\(\frac{d}{dx}\log_a x=\frac{1}{x\ln a}\),特别地,\(\frac{d}{dx}\ln x=\frac{1}{x}\)。
- \(y=\log_a x\),则 \(a^{y}=x\)。同时对 \(x\) 求导得 \(a^{y}\ln a\cdot y'=1\),移项即得 \(y'=\frac{1}{x\ln a}\)。
微分
定义
对于 \(y=f(x)\),如果当 \(\Delta x\to 0\) 时,\(\Delta y\) 可表示为 \(\Delta y=k\Delta x+o(\Delta x)\),其中 \(k\) 是常量,则称 \(f(x)\) 在 \(x_0\) 处可微,\(dy=k\Delta x\),记为微分符号。
\(k=\lim\limits_{\Delta x\to 0}\frac{dy}{\Delta x}=\lim\limits_{\Delta x\to 0}\frac{k\Delta x+o(\Delta x)}{\Delta x}=\lim\limits_{\Delta x\to 0}\frac{\Delta y}{\Delta x}=f'(x_0)\)。
定理
罗尔定理、拉格朗日中值定理、柯西中值定理。
洛必达法则
若
- \(\lim\limits_{x\to a}f(x)=\lim\limits_{x\to a}F(x)=0\)
- \(f(x)\) 与 \(F(x)\) 在 \(a\) 的某去心邻域可导且 \(F'(x)\ne 0\)
- \(\lim\limits_{x\to a}\frac{f'(x)}{F'(x)}=A\)
则 \(\lim\limits_{x\to a}\frac{f(x)}{F(x)}=\lim\limits_{x\to a}\frac{f'(x)}{F'(x)}=A\)。
以上是 \(0\) 比 \(0\) 型,也有 \(\infty\) 比 \(\infty\) 型。
泰勒中值定理
若函数 \(f(x)\) 在包含 \(x_0\) 在内的开区间 \((a,b)\) 内 \((n+1)\) 阶可导,则对 \(\forall x\in(a,b)\) 有
其中(\(\xi\) 为 \(x\) 与 \(x_0\) 之间的某个值)
上式称为 \(f(x)\) 在 \(x_0\) 处展开的 \(n\) 阶泰勒公式,其中 \(R_n(x)\) 称为拉格朗日余项。
特别地,如果 \(f(x)\) 无穷阶可导,则它的无穷阶泰勒公式
称为它的泰勒级数。
在泰勒公式中,取 \(x_0=0\),得到麦克劳林公式
常见函数的麦克劳林展开:
- \(e^x=1+x+\frac{x^2}{2!}+\cdots+\frac{x^n}{n!}+\cdots\)
- \(\sin x=x-\frac{x^3}{3!}+\frac{x^5}{5!}\cdots+(-1)^{n-1}\frac{x^{2n-1}}{(2n-1)!}+\cdots\)
- \(\cos x=1-\frac{x^2}{2!}+\frac{x^4}{4!}+\cdots +(-1)^n\frac{x^{2n}}{(2n)!}\)+\(\cdots\)
积分
本质是求导的逆运算。
不定积分
若 \(F'(x)=f(x)\) 或 \(d(F(x))=f(x)dx\),则 \(F(x)\) 为 \(f(x)\) 的一个原函数。将 \(f(x)\) 的原函数集合(原函数加常数仍是原函数)称为它的不定积分,记
不定积分表(部分)
第一类换元积分法(凑微分)
关键在于把 \(f(x)\) 凑成 \(g(u)u'\)。
如
第二类换元积分法
第一类反过来:
例:
还有三角代换、倒代换。
分部积分法
由 \((uv)'=u'v+uv'\) 得
可简写为
\(u\) 的选取遵循“反对幂指三”。
定积分
可以看作 \([a,b]\) 区间围成图形的面积。
可以求弧长、体积、侧面积,在 OI 中可用来估计时间复杂度。
牛顿-莱布尼茨公式
微积分基本定理。
其中 \(F(x)\) 为 \(f(x)\) 在 \([a,b]\) 上的原函数。
斯特林公式
\(n!\sim \sqrt{2\pi n}(\frac{n}{e})^n\),可以用来估计时间复杂度。
数值积分
用于求定积分的近似值。
辛普森公式:
以及自适应辛普森算法。
快速傅里叶变换
FFT
对于多项式 \(F(x)=\sum\limits_{i=0}^{n-1}a_ix^{i}\),\(G(x)=\sum\limits_{i=0}^{m-1}b_ix^{i}\),求 \(H=F*G\)。
设 \(H=\sum\limits_{i=0}^{n+m-2}c_ix^i\),由于这是关于 \(x\) 的 \(n+m-2\) 次函数。朴素的话有 \(c_i=\sum\limits_{j=0}^{i}a_jb_{i-j}\)。
考虑快速求出 \(H\) 的 \(n+m-1\) 个点值,再快速求出 \(H\)。
DFT
因为 \(H(x)=F(x)\cdot G(x)\),考虑选出 \(N=n+m-1\) 个点 \(x_0,\cdots,x_{N-1}\),考虑 \(F\),有
直接算时间复杂度是 \(O(N^2)\) 的,考虑递归的思想。
通过神奇的观察可以发现,当 \(x_i=\omega_{N}^i\) 时,左边矩阵不同的数只有 \(N\) 个,接下来就好办了。
令 \(x=\omega_n^{i}\)。定义 \(F_0(x)\) 和 \(F_1(x)\)。
于是有 \(F(x)=F_0(x^2)+xF_1(x^2)\)。令 \(m=\frac{n}{2}\),容易发现 \((\omega_{n}^{i})^2=\omega_{m}^{i}\),所以发现变成子任务,然后分类讨论:
- \(0\le i<m\),\(F(\omega_{n}^{i})=F_0(\omega_{m}^i)+\omega_{n}^{i}F_1(\omega_{m}^{i})\)。
- \(0\le i<m\),\(F(\omega_{n}^{i+m})=F(-\omega_{n}^{i})=F_0(\omega_{m}^{i})-\omega_{n}^{i}F_1(w_{m}^{i})\)
\(F_0\) 和 \(F_1\) 都可以递归算,所以时间复杂度为 \(O(n\log n)\),为了方便常把 \(N\) 补成 \(2\) 的幂次。
IDFT
已知左边矩阵和答案矩阵求系数矩阵,容易发现即求左边矩阵的逆矩阵。
经过观察可以发现,令左边矩阵为 \(X\),\(X_{i,j}=\omega_{N}^{ij}\),则 \(X^{-1}_{i,j}=\frac{1}{N}\omega_{N}^{-ij}\)。通过直接计算可以证明。
实现细节
-
可以手写复数类,容易发现 \(\omega_{N}^{i}\) 与 \(\omega_{N}^{-i}\) 互为共轭,所以 DFT 和 IDFT 可以调用一个函数,传一个参进去即可。
-
可以去掉递归,只需要求出每个位置 \(i\) 在底层时对应哪个位置 \(i'\) 即可,通过观察可以发现 \(i'\) 事实上是 \(i\) 的二进制翻转。然后模拟递归过程,从底层开始,每次合并 \(2^{k}\) 个数。
-
对于 \(F_0\) 和 \(F_1\),将 \(F_0(\omega_{m}^{i})\) 的值存在 \(i\) 的位置,\(F_1(\omega_{m}^{i})\) 的值存在 \(m+i\) 的位置,这样就不用新开空间了。
-
求单位根。如果已知 \(0\sim m-1\) 的 \(\omega_{m}^{i}\),那么可以直接算出 \(\omega_{n}^{i}\),对于 \(i\equiv 0\pmod 2\),\(\omega_{n}^{i}=\omega_{m}^{i/2}\),否则只需要先通过欧拉公式求出 \(\omega_{n}\),则 \(\omega_{n}^{i}=\omega_{n}^{i-1}\omega_n\)。
void fft(comp a[],int n,int ty){
for(int i=0,j=0;i<n;i++){
if(i<j)swap(a[i],a[j]);
for(int k=n>>1;(j^=k)<k;k>>=1);
}
w[0]=comp{1,0};
for(int i=1;i<n;i<<=1){
int t=i*2;
comp x=comp{cos(2*pi/t),ty*sin(2*pi/t)};
for(int j=t-2;j>=0;j-=2)w[j]=w[j>>1],w[j+1]=w[j]*x;
for(int j=0;j<n;j+=t){
for(int k=j;k<j+i;k++){
comp t0=a[k],t1=a[k+i]*w[k-j];
a[k]=t0+t1;
a[k+i]=t0-t1;
}
}
}
if(ty==-1){
for(int i=0;i<n;i++)a[i].a/=n,a[i].b/=n;
}
}
例题
01序列问题
给出两个长度分别为 \(n\)、\(m\) 的 01 序列 \(A\)、\(B\),有 \(Q\) 次询问,每次询问当把 \(A\) 的第 \(i\) 位和 \(B\) 的第 \(j\) 位对齐时,\(A\),\(B\) 公共部分有多少对对齐且相同的数。
不如将 \(B\) 序列翻转得到 \(B'\),则 \(A\times B'\) 的 \(i\) 次项就是 \(\sum\limits_{j=0}^{i}A_{j}B'_{i-j}\),即将 \(A\) 与 \(B\) 的第 \(m-i\) 位对齐都是 \(1\) 的位置数。
然后分别将 01 取反再跑一次就得到都是 \(0\) 的位置数。
[HNOI2017] 礼物
如果将 \(A\) 序列加 \(x\)(这里允许 \(x\) 为负数,相当于 \(B\) 序列加 \(-x\)),则答案为
容易发现前 \(2\) 项为定值,中间 \(2\) 项可以通过枚举 \(x\) 算,只需要考虑求最后一项的最大值。
将 \(B\) 翻转并复制一份即可。
快速数论变换
NTT
对于 FFT,只能处理 \(10^4\times 10^4\times 10^6\) 级别问题,其中 \(10^4\) 是值域,\(10^6\) 是长度。否则会爆 double。
如果题目中有模数且可以用 NTT,则能处理 \(p\times p\times 10^6\) 级别问题。并且将复数运算代替为取模运算,常数更加优秀。
考虑在模意义下的多项式乘法,如果模数 \(p\) 有原根,那么本质上原根和单位根是相同的。\(g^{0,1,\cdots,p-2}\) 相当于 \(\omega_{p-1}^{0,1,\cdots,p-2}\),都是互不相同且能达到效果的。并且易得 \(\omega_{n}=g^{\frac{p-1}{n}}\),因为 \(n\) 次方均为 \(1\)。也就是说当 \(n\mid p-1\) 时,单位根才存在。
而在 DFT 和 IDFT 的过程中,要求长度是 \(2\) 的幂次,也就是说要满足 \(2^k\mid p-1\),这也就限制了一些模数不可取。
常见的模数有
- \(998244353=7\times 17\times 2^{23}+1\),原根为 \(3\),能处理长度 \(\le 2^{23}\)。
- \(1004535809=479\times 2^{21}+1\),原根为 \(3\)。
- \(469762049 = 7\times 2^{26}+1\),原根为 \(3\)。
在 IDFT 时,要求 \(\omega_n^{-i}\),本质就是 \(g^{-\frac{p-1}{n}}=(g^{-1})^{\frac{p-1}{n}}\)。
void ntt(int a[],int n,int ty){
for(int i=0,j=0;i<n;i++){
if(i<j)swap(a[i],a[j]);
for(int k=n>>1;(j^=k)<k;k>>=1);
}
g[0]=1;
for(int i=1;i<n;i<<=1){
int t=i*2;
int x=ksm(ty==1?3:(P+1)/3,(P-1)/t);
for(int j=t-2;j>=0;j-=2)g[j]=g[j>>1],g[j+1]=(ll)g[j]*x%P;
for(int j=0;j<n;j+=t){
for(int k=j;k<j+i;k++){
int t0=a[k],t1=(ll)a[k+i]*g[k-j]%P;
a[k]=mod(t0+t1);
a[k+i]=mod(t0+P-t1);
}
}
}
if(ty==-1){
int x=ksm(n,P-2);
for(int i=0;i<n;i++)a[i]=(ll)a[i]*x%P;
}
}
差卷积
在一些题目当中会出现 \(c_i=\sum\limits_{j=i}^{n}a_{j}b_{j-i}\) 的形式,因为 \(j-(j-i)=i\),所以叫做差卷积。
只需
其中 \(a'\) 为 \(a\) 的翻转。发现相当于求 \(a'\) 与 \(b\) 的和卷积的第 \(n-i\) 项。
任意模数卷积
值域 \(\le 10^9\),\(n\le 10^5\)。
9 NTT
因为不保证模数是 NTT 模数,所以不能直接 NTT。
考虑到最后答案 \(\le 10^{23}\),所以可以三模 NTT。即取三个 NTT 模数分别得出结果,然后 crt 即可求出真实答案,再进行对模数取模。
8 FFT
因为答案太大,会爆 double,考虑拆位。
将 \(x\) 拆成 \(\lfloor\frac{x}{s}\rfloor s+x\bmod s\),其中 \(s=\sqrt{10^9}\)。然后 \(A_1\) 存前半部分,\(A_2\) 存后半部分,那么 \(A*B=(A_1s+A_2)*(B_1s+B_2)=A_1*B_1s^2+(A_1*B_2+A_2*B_1)s+A_2*B_2\)。
这样卷积答案就只有 \(10^{14}\) 量级,但是总共要跑 \(8\) 次 FFT。
5 FFT
考虑构造复数多项式 \(F=A_1+A_2\mathrm{i}\),\(G=A_1-A_2\mathrm{i}\) 和 \(H=B_1+B_2\mathrm{i}\)。发现通过算出 \(F*H\) 和 \(G*H\) 可以解出 \(8\) FFT 需要的值。总共只有 \(5\) 次 FFT。
\(O(\text{5 FFT})<O(\text{9 NTT})\)。
4 FFT 去哪了呢?
快速沃尔什变换
解决 \(c_i=\sum\limits_{j\oplus k=i}a_jb_k\),其中 \(\oplus\) 是一个位运算,如 \(\mathbf{xor}\)、\(\mathbf{or}\) 和 \(\mathbf{and}\)。
思想
异或卷积
考虑构造线性变换 \(\mathcal{T}\) 使得 \(\mathcal{T}(F)_i\cdot \mathcal{T}(G)_i=\mathcal{T}(H)_i\)。相当于对于一个向量乘以矩阵 \(M\) 得到一个新的向量。可以这样写
可以变形得到
于是可以得到 \(M_{j,i}M_{k,i}=M_{j\ \mathbf{xor}\ k,i}\)。然后经过一顿推导,发现
然后就可以 \(O(n\log n)\) 算了。逆变换相当于解二元一次方程,最后除以 \(n\) 即可。
void fwt(int a[],int n,int ty){
for(int i=1;i<n;i<<=1){
int t=i*2;
for(int j=0;j<n;j+=t){
for(int k=j;k<j+i;k++){
int t0=a[k],t1=a[k+i];
a[k]=mod(t0+t1);
a[k+i]=mod(t0+p-t1);
}
}
}
if(ty==-1){
int x=ksm(n,p-2);
for(int i=0;i<n;i++)a[i]=(ll)a[i]*x%p;
}
}
或卷积
同理可以得到 \(M_{j,i}M_{k,i}=M_{j\ \mathbf{and}\ k,i}\),构造 \(M_{x,y}=[x\subseteq y]\) 满足条件。
那么 \(\mathcal{T}(F)_i=\sum\limits_{j\subseteq i}a_j\),相当于做高维前缀和,有
逆变换易得。时间复杂度 \(O(n\log n)\)。
void fwt(int a[],int n,int ty){
for(int i=1;i<n;i<<=1){
int t=i*2;
for(int j=0;j<n;j+=t){
for(int k=j;k<j+i;k++){
a[k+i]=mod(a[k]*ty+a[k+i]);
}
}
}
}
与卷积
对于与卷积,与或卷积类似,构造 \(M_{x,y}=[y\subseteq x]\) 满足条件。然后即做高维后缀和,有
代码与或卷积类似。
子集卷积
求
等价于
其中 \(|x|\) 表示 \(\text{popcount}(x)\)。
把长度看成 \(2^n\)。如果只有第一个条件我们可以 \(O(2^n n)\) 求出,考虑增加第二个条件。
发现有点类似和卷积的形式,新开一维状态表示 \(1\) 的个数,有
于是我们便可以枚举 \(w=0\sim n\) 求出 \(\mathcal{T}(F_{w})\) 和 \(\mathcal{T}(G_{w})\),然后用 \(O(2^{n}n^2)\) 的时间复杂度合并,然后再每一位逆变换回去即可。总体时间复杂度 \(O(2^nn^2)\)
常见用于优化状压 dp。
多项式全家桶
https://www.luogu.com.cn/paste/1kz4jufv
多项式求逆
已知 \(A(x)\),求 \(B(x)\) 使 \(A(x)B(x)\equiv 1\pmod {x^n}\)。
取模是因为 \(B\) 可能有无限多项,故只取前 \(n\) 项。\(O(n^2)\) 暴力是容易的,直接递推即可;考虑用倍增优化。
假设已经求出 \(B\) 的前 \(m\) 项,记为 \(B_m\),考虑求出 \(B_{2m}\)。我们可以得到到两个式子:
将第二个式子平方可得
再同时乘上 \(A(x)\),由第一个式子得
通过移项可得
每次计算时 \(A\) 只取前 \(2m\) 项,那么时间复杂度为 \(\sum\limits_{i=1}^{\log_2 n}i2^i=n\log n\)。
多项式除法
已知 \(A(x)\) 和 \(B(x)\),求 \(D(x)\) 和 \(R(x)\) 使得
假设 \(A(x)\) 为 \(n\) 次,\(B(x)\) 为 \(m\) 次,则要求 \(R(x)\) 低于 \(m\) 次。\(D(x)\) 显然是 \(n-m\) 次,令 \(R(x)\) 为 \(m-1\) 次(高位如果不存在用 \(0\) 补位)。
考虑将系数翻转,即 \(A'(x)=A(x^{-1})x^n\),因为
所以
可以得到
于是可以算出 \(D'(x)\),且是 \(n-m\) 次的,符合要求。
然后 \(R(x)=A(x)-B(x)D(x)\)。总时间复杂度 \(O(n\log n)\)。
线性递推
已知 \(f_0\sim f_{m-1}\),\(d_{0}\sim d_{m-1}\),且
求 \(f_{n}\)。
通过矩阵快速幂可以做到 \(O(m^3\log n)\),考虑优化。
有特征方程 \(x^{m}=\sum\limits_{i=0}^{m-1}x^{m-i-1}d_i\)。任何 \(f_{n}\) 都可以用 \(f_{0\sim m-1}\) 表示,所以只需求出 \(x^{n}\)。
考虑倍增,已知 \(x^{k}\),如何求出 \(x^{2k}\)。
将两边同时平方,需要将 \(x^{m\sim 2m-2}\) 的项进行降幂处理。事实上,这个操作等价于将它对 \((x^{m}-\sum\limits_{i=0}^{m-1}x^{m-i-1}d_i)\) 取模,因为该式结果为 \(0\),而降幂本质上是不断减去这个多项式乘以一个整式,而结果不变,所以是等价的。
取模用多项式除法即可,于是总时间复杂度为 \(O(m\log m\log n)\)。
多项式多点求值
已知 \(F(x)\),求 \(F(x_{1\sim m})\)。
先考虑在 \(m\) 较小的情况下,想到构造 \(G(x)\) 使得 \(G(x_i)=F(x_i)\),且 \(G(x)\) 的次数不超过 \(m\) 次。
仿照线性递推降幂的思路,由于
对于 \(x=x_i\) 结果为 \(0\),且次数为 \(m\),所以将 \(F(x)\) 对 \(A(x)\) 取模后便能得到 \(G(x)\)。时间复杂度瓶颈在带值 \(O(m^2)\)。
考虑进行分治。令 \(A_{l,r}(x)\) 为 \(\prod\limits_{i=l}^{r}(x-x_i)\),那么将 \(F(x)\) 从上至下一直取模到 \(A_{i,i}(x)\),容易发现此时的 \(F(x)\) 就是 \(F(x_i)\),因为 \(F(x)\) 只剩常数项,且取模不改变 \(F(x_i)\) 的值。时间复杂度为 \(O(n\log^2 n)\)。
转置原理优化
https://www.luogu.com.cn/article/5l5gpirn
多项式开方
已知 \(A(x)\),求 \(B(x)^2\equiv A(x)\pmod {x^{n}}\)。
暴力可以 \(O(n^2)\) 做,仿照求逆的思路,考虑倍增。
假设已知 \(B_m(x)\),求 \(B_{2m}(x)\)。可以得到
因为
所以
多项式求导/积分
设 \(F(x)=\sum\limits_{i=0}^{n}a_ix^i\),根据求导和积分法则可以得到
时间复杂度显然为 \(O(n)\)。
多项式 ln(exp)
已知 \(A(x)\),求 \(B(x)=\ln A(x)\),即 \(e^{B(x)}=A(x)\)。
\(B(x)\) 存在的条件为 \([x^0]A(x)=1\),否则找不到 \(e^k\equiv [x^0]A(x)\pmod p\)。
考虑先对 \(\ln A(x)\) 求导,然后再积分回去
总时间复杂度为 \(O(n\log n)\)。
已知 \(A(x)\),求 \(B(x)=e^{A(x)}\)。
\(B(x)\) 存在的条件为 \([x^0]A(x)=0\)。
求解可以通过牛顿迭代。
牛顿迭代指的是,已知 \(G(x)\),求 \(F(x)\) 使得 \(G(F(x))\equiv 0\pmod {x^n}\)。
比如说想求 \(B(x)\equiv \sqrt {A(x)}\pmod {x^n}\),则构造 \(G(B(x))=A(x)-B^2(x)\),考虑如何求解。
考虑倍增,已知 \(B_m(x)\),如何求 \(B_{2m}(x)\)。
我们让 \(G(B_{2m}(x))\) 在 \(B_{m}(x)\) 处泰勒展开
\[G(B_{2m}(x))=\sum_{i=0}^{\infty}\frac{G^{(i)}(B_{m}(x))}{i!}(B_{2m}(x)-B_m(x))^i\equiv 0\pmod {x^{2m}} \]容易发现 \(B_{2m}(x)-B_m(x)\) 的最低次至少 \(x^{m}\),所以当 \(i\ge 2\) 时在 \(\bmod x^{2m}\) 后都变成 \(0\) 了,所以又可以写成
\[G(B_{2m}(x))\equiv G(B_{m}(x))+G'(B_{m}(x))(B_{2m}(x)-B_{m}(x))\pmod {x^{2m}} \]然后便得到
\[B_{2m}(x)\equiv B_{m}(x)-\frac{G(B_m(x))}{G'(B_m(x))}\pmod {x^{2m}} \]
回归本题,只需构造 \(G(B(x))=A(x)-\ln B(x)\) 即可。注意 \(G\) 求导是关于 \(B_m(x)\),所以 \(A(x)\) 是常量。
时间复杂度 \(O(n\log n)\)。
多项式快速幂
已知 \(A(x)\),求 \(B(x)=A^k(x)\bmod x^{n}\)。
考虑两边取对数,得 \(\ln B(x)=k\ln A(x)\),算出 \(\ln B(x)\) 后再 \(\exp\) 回去,接下来讨论两个问题:
- \(k\) 应该对 \(p\) 取模。考虑 \(e^{x}\) 的泰勒展开,将 \(x\) 替换为 \(k\ln A(x)\),发现 \(x\) 并不在指数位,所以可以直接取模。
- \([x^0]A(x)\ne 1\)。显然不能直接求 \(\ln\),找到 \(A(x)\) 的最低次有效项 \(a_ix^i\),所以\[\begin{aligned} B(x)&=\left(a_ix^i\frac{A(x)}{a_ix^i}\right)^k\\ &=a_i^kx^{ik}\exp\left(k\ln \frac{A(x)}{a_ix^i}\right) \end{aligned} \]因为对 \(x^{n}\) 取模,所以若 \(ik\) 太大则结果为全 \(0\) 多项式。
快速拉格朗日插值
我们要求
观察到分母和 \(i,j\) 都有关,尝试拿出来单独求
容易发现对于 \(k\ne i\),\(g_k(x_i)=0\),所以可以把所有的 \(g_i(x)\) 加起来,不影响 \(g_i(x_i)\) 的值。
这里已经可以直接分治 NTT 求了,但是常数较大,注意到
所以只需要分治 NTT 求出 \(H(x)\) 后求导即得 \(G(x)\),然后用多项式多点求值求出 \(G(x_{1\sim n})\)。
现在即求
直接上分治 NTT,有
总时间复杂度 \(O(n\log ^2 n)\)。
生成函数
解决无标号计数问题常用普通生成函数,有标号计数问题则是指数生成函数。
普通生成函数
对于数列 \(a\),定义其普通生成函数(OGF)为
容易发现 \(F(x)G(x)\) 为数列 \(\sum_{i=0}^{n}a_ib_{n-i}\) 的 OGF。
常见封闭形式有:
-
等比数列
\[\sum_{n\ge 0}p^nx^n=\frac{1}{1-px} \] -
组合数列
\[\sum_{n\ge 0}\binom{m+n-1}{n-1}=\frac{1}{(1-x)^m}=\left(\sum_{n\ge 0}x^n\right)^{m} \]可用隔板法证明。
-
等差幂数列
\[\sum_{n\ge 0}x^{nk}=\frac{1}{1-x^k} \]
将形式幂级数转换为封闭形式的作用是方便乘除。
指数生成函数
对于数列 \(a\),定义其指数生成函数(EGF)为
容易发现 \(\hat{F}(x)\hat{G}(x)\) 为数列 \(\sum_{i=0}^{n}\binom{n}{i}a_ib_{n-i}\) 的 EGF。有组合意义。
注意到
因为 \(e^x\) 的麦克劳林展开为它。
EGF 与 EXP 的组合意义
设 \(\hat{F}(x)\) 是 \(f(x)\) 的 EGF。
如果 \(\exp \hat{F}(x)=\hat{G}(x)\),则 \(\hat{G}(x)\) 是
或者说
的 EGF。
代数角度推导:
求 \(n\) 个点的简单有标号无向连通图数量。以下均是有标号的前提下。
设 \(f(i)\) 为 \(i\) 个点的简单无向连通图数量,\(g(i)\) 为 \(i\) 个点的简单无向图数量。所以 \(g(i)=2^{\binom{i}{2}}\),即看每条边选不选。令 \(\hat{F}(x)\) 和 \(\hat{G}(x)\) 分别为它们的指数生成函数。
看上面两个式子,\(g(i)\) 显然可以拆分成若干个连通块,所以符合第一个式子;然后考虑暴力,枚举一个连通块,其他的点随便连。但是为了避免算重,固定枚举 \(1\) 号点所在连通块的大小,并在剩下 \(n-1\) 个点中选出在连通块内的点,于是便能得到第二个式子。通过第二个式子便能在 \(O(n^2)\) 的时间复杂度内求得 \(f(n)\)。
如果用上面的结论,就有 \(\exp \hat{F}(x)=\hat{G}(x)\),如果是对的就能直接通过取 \(\ln\) 算出 \(F(x)\),考虑一下它的组合意义:
考虑 \([x^k]\)。分子表示的是 \(k\) 个点包含 \(i\) 个有序连通块的方案数,那么除以 \(i!\) 即表示 \(k\) 个点包含 \(i\) 个无序连通块的方案数,那么相加起来即表示 \(k\) 个点包含若干无序连通块的方案数,也就正好是 \([x^k]\hat{G}(x)\)。所以就能对应上。
应用
有 \(m\) 种物品,体积为 \(a_i\),数量为 \(b_i\),对 \(1\sim n\) 求填满容积为 \(x\) 背包的方案数。
设 \(f(i)\) 为容积为 \(i\) 的方案数,\(F(x)\) 为它的普通生成函数。容易发现
要求 \(F(x)\),考虑两边取 \(\ln\),
考虑如何求 \(\ln (1-x^{k})\),可以求导后积分回去,即
所以
我们只关心 \([x^k]F(x)(k\le n)\),所以如果枚举 \(i\) ,枚举量是 \(O(n\log n)\) 级别的,求出系数后再 \(\exp\) 回去即可。总时间复杂度 \(O(n\log n)\)。
有集合 \(S\),已知 \(f(1\sim n)\) 为 \(i\) 表示成 \(S\) 中元素之和的方案数,求 \(S\)。
仿照上题,\(s_k=[k\in S]\),有
写成
每次比较 \(x_i\) 的系数然后更新即可。
已知 \(a_{1\sim m}\),\(f(k)=\sum a_i^k\),求 \(f(1\sim n)\)。
设 \(s_k=\sum_{i=1}^{m}[a_i=k]\)。
分式相加不太好处理,考虑同时积分
有个 \(\frac{1}{a_i}\) 不太好搞,考虑把 \(F(x)\) 写为
然后就变成
然后分治 NTT 即可。

浙公网安备 33010602011771号