基 础 多 项 式 与 基 础 生 成 函 数
噔 噔 噔。
你说的对,但是确实挺基础的说。
很多东西避开了严谨的描述。
文章内容非常浅(che)显(dan)。
prework
卷积 :\(a,b\)为两个数列,两个数列的卷积 \(c\) 有 \(c_k=\sum\limits_{i=1}^{k} a_{i}b_{k-i}\)。
多项式的点值表示:\(n+1\) 个坐标确定一个 \(\le n\) 次的多项式,我们可以用这 \(n+1\) 个坐标表示。
注意到直接用点值表示出来的话,能快速的得到相乘的多项式的点值表示。
我们实际上是计算特殊的点值。
单位根:\(x^n-1 =0\) 我们称 \(x\) 为 \(n\) 次单位根,其中 $n \in \mathbb{C} $。我们设 $ \omega $,根据代数基本定理,它在复数集下显然有 \(n\) 个解。我们分别记最小解为 $ \omega_1,\omega_2,\omega_3 \ldots \omega_n$,实际上就是每个角为 \(\frac{2\pi}{n}\),这个东西定义为 \(\omega_{n} = e^{\frac{2\pi i}{n}}\)
$ \because e^{i x } = \cos x+ i\sin x $(欧拉公式)
\(\therefore \omega_n = \cos \frac{2\pi}{n} + i\sin \frac{2\pi}{n}\)
FFT的推导过程
离散傅里叶变换(DFT)
是长这样的,\(a\)是一个数列,对于 \(0 \le k < n\),我们令
我们记 \(b=F(a)\)
离散傅里叶变换的逆变换(IDFT)
顾名思义,直接给出式子
证明有点长,大概就是根据定义去推。
意思就是说如果我们知道单位根的点值,就能推出原多项式的系数了。
循环卷积
有 \(a,b\) 两个数列,
\(c_k=\sum\limits_{i+j \bmod n=k} a_{i}b_{k-i}\)
我们记作 $ c = a * b$
我们有一个性质 \(F(a*b)=F(a) \times F(b)\)
补充单位根的两个性质
Lemma 1:\(({\omega_{2n}}^k)^2={\omega_{n}}^k\)
Lemma 2:\({\omega_{2n}}^{n+k}={\omega_{2n}}^{-k}\)
你把复平面建立出来证明式容易的。
我们对多项式 \(A(x)\) 开始进行操作,我们记 \(n=2m\)。
注意到有相同的,这是好事。
那我们定义\(A_0(x)=\sum\limits_{0\le i < m} a_{2i}x^{2i} ,A_1(x)= x\sum\limits_{0\le i < m} a_{2i+1}x^{2i}\)
蝴蝶变换
考虑将前面的东西结合一下?下面还是有 \(0 \le k < m\)
通过蝴蝶操作的过程可以看出,如果我们得到了 \(A_0(x),A_1(x)\)
在点\({\omega_{m}}^0,{\omega_{m}}^1 \ldots {\omega_{m}}^{m-1}\),我们可以在 \(O(n)\) 时间内
计算出 \(A(x)\) 在 \({\omega_{m}}^0,{\omega_{m}}^1 \ldots {\omega_{m}}^{m-1}\),我们可以在 \(O(n)\)处的点值。
而计算 \(A_0,A_1\) 的点值的过程可以递归分治进行。
根据主定理,我们可以在 \(O(n\log n)\) 的时间内求出所要的 \(A(x)\) 的点值表示。
为了快速的进行 IDFT,对比 DFT与 IDFT的表达式,可以发
现我们只需要将FFT过程中用到的 \(\omega_{n}\) 全部替换为 \({\omega_{n}}^{-1}\),最后再乘以 \(\frac{1}{n}\) 即可。
令 \(rev(x)\) 表示将 \(x\) 的二进制位的顺序反转之后得到的数字,我们每次 将 \(a_i = a_{rev(i)}\) 则每次需要对 \(a\) 进行的蝴蝶操作在 \(a′\) 中变成了对两个相邻的序列的操作。方便多了。我觉得你记忆模板就行。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+6,bmod=1e9+7,INF=2e9;
// 省略了缺省源,想看找我要就行了
struct CP{
db x,y;
CP(db X=0.0,db Y=0.0){x=X,y=Y;}
inline CP operator +(const CP &p){return CP(x+p.x,y+p.y);}
inline CP operator -(const CP &p){return CP(x-p.x,y-p.y);}
inline CP operator *(const CP &p){return CP(x*p.x-y*p.y,y*p.x+x*p.y);}
}f[N<<1],g[N<<1];
int rev[N<<1];
int n,m;
const db pi=acos(-1);
void FFT(CP *f,db opt){
rep(i,0,n-1) if(i<rev[i]) swap(f[i],f[rev[i]]);
for(int p=2;p<=n;p<<=1){
int len=(p>>1);
CP wd(cos(2*pi/p),sin(2*pi/p)*opt);
static CP w[N];
w[0]={1,0};
rep(i,1,len) w[i]=w[i-1]*wd;
for(int k=0;k<n;k+=p){
rep(l,k,k+len-1){
CP now=f[l+len]*w[l-k];
f[l+len]=f[l]-now,f[l]=f[l]+now;
}
}
}
}
int main(){
cin>>n>>m;
rep(i,0,n) cin>>f[i].x;
rep(i,0,m) cin>>g[i].x;
for(m+=n,n=1;n<=m;n<<=1);
rep(i,0,n) rev[i]=(rev[i>>1]>>1)|((i&1)?(n>>1):0);
FFT(f,1);FFT(g,1);
rep(i,0,n) f[i]=f[i]*g[i];
FFT(f,-1);
rep(i,0,m) cout<<(int)(fabs(f[i].x)/(n*1.0)+0.5)<<" ";
}
NTT
一些定义
阶:对于正整数 \(m,a\),满足 \(\gcd(a,m)=1\), 使得 \(a^{n} \equiv 1 (\bmod m)\),则最小的正整数 \(n\) 称为 \(a\) 模 \(m\) 的阶,记作\(\delta_{m}(a)\)。
原根:如果$\delta_{m}(a) =\varphi(m) $,则 \(a\) 是 m 的一个原根。只有模\(2\),\(4\),\(p^a\),\(2p^a\) 存在原根。
设 \(m > 1\),\(g\) 为正整数且 \(gcd(g,m) = 1\)。则 \(g\) 是 \(m\) 的原根当且仅当对于任意 \(\varphi(m)\) 的质因子 \(q_i\),有 \(g^{\frac{\varphi(m)}{q_i}} \not\equiv 1 (\bmod \ m)\)。
暴力找是对的,量级是 \(O(n^{0.25})\)
这里并没有其他内容,后面再补吧。
假设质数 \(p\) 满足 \(p = r \times 2^x + 1\),\(g\) 是 \(p\) 的原根
使用 $g^{\frac{p-1}{n}} $ 带替之前的 \(\omega\),就是 NTT 了。证明略了。
这里应该有一张原根表。
分治FFT/NTT
分治 + FFT,洛谷板子也差不多,略。
全家桶
你好,这个是不是有点不太基础了。
多项式牛顿迭代
有一多项式\(g(x)\),一形式幂级数\(f(x)\),如果有 \(g(f(x))\equiv 0 (\bmod \ x^{n})\),那我们有
接下来都是默认在模意义下的。
证明需要使用泰勒展开,我们首先有 \(g(f0(x))\equiv 0 (\bmod \ x^{n})\)
实际上第三项之后就是 \(0\),所以
变形可以得到
实际上证明并不重要,接下来才是好玩的。
多项式求逆(乘法逆)
有多项式 \(h(x)\),若\(f(x)h(x) \equiv 1(\bmod \ x^n)\),\(f(x)\) 为 \(h(x)\) 的乘法逆。
设多项式\(G(x)\),\(G(f(x))=\frac{1}{f(x)} -h(x)\)
套公式得$ f(x) = f_0(x) - \frac{G(f_0(x))}{G'(f_0(x))}$,然后 \(f(x) = f_0(x) - \frac{\frac{1}{f_0(x)} -h(x)}{-\frac{1}{{f_{0}(x)}^2}}\)
得到结论 $ f(x) \equiv f_0(x) - {f_0}^2(x)h(x) \ (x \bmod \ x^{2n})$
继续咕咕。
生成函数
咕咕
一些题目
题目先咕咕了,后面来写,可能会重开一篇。
P1919 【模板】高精度乘法 | A*B Problem 升级版
注意到高精度乘法本身就是一个卷积,直接把每一位当系数卷积即可,要处理一下进位。
P3338[ZJOI2014]力
咕咕。
P3723 [AH2017/HNOI2017] 礼物
题目是搞笑的,笔者看错题了,笔者更搞笑。
我们考虑一下增量为 \(\Delta\) 会发生什么。
\(\sum\limits_{i=1}^n=(x_i-y_i+\Delta)^2 = \sum\limits_{i=1}^n({x_i}^2+{y_i}^2-2x_iy_i-2\Delta(x_i-y_i) +\Delta^2)= \sum\limits_{i=1}^n({x_i}^2+{y_i}^2 )-\sum\limits_{i=1}^n2x_iy_i-2\Delta\sum\limits_{i=1}^n(x_i)-\sum\limits_{i=1}^n(y_i)+n\Delta^2\)
枚举 \(\Delta\),只有 \(\sum\limits_{i=1}^n2 x_i y_i\) 不是定值。
考虑怎么求这个东西的最值,如果没有题目上的限制的话是P1966[NOIP2013 提高组] 火柴排队,我们设它旋转了 \(k\) 次 ,第 \(k\) 次的和就是 \(\sum\limits_{i=1}^n2 x_{i+k +[i+k>n] \times -1} y_i\) 确实太不好看了,断环为链,倍长出去就是 \(\sum\limits_{i=1}^n2 x_{i+k } y_i\)
经典Trick,直接翻转 \(x\),得到\(\sum\limits_{i=1}^n2 x_{2n-i-k } y_i\),这个就是卷积啊,答案就在 \(n+1\) 到 \(2n\) 之间的最值。那么就做完了。