FFT
迭代实现,计算卷积。
void FFT(int limit, complex *a, int type)
{
complex x, y;
for (int i = 0; i < limit; ++i)
if (i < r[i])
std::swap(a[i], a[r[i]]);
for (int mid = 1; mid < limit; mid <<= 1)
{
complex Wn(cos(pi / mid), type * sin(pi / mid)), x, y;
for (int j = 0; j < limit; j += (mid << 1))
{
complex w(1, 0);
for (int k = 0; k < mid; ++k, w = w * Wn)
{
x = a[j + k];
y = w * a[j + mid + k];
a[j + k] = x + y;
a[j + mid + k] = x - y;
}
}
}
}
void polymulti(int n, int m, complex F[], complex G[])
{
int limit, i,len;
for (limit = 1,len = 0; limit <= n + m; limit <<= 1, ++len)
;
for (i = 0; i < limit; ++i)
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (len - 1)); //二进制反转
FFT(limit, F, 1);
FFT(limit, G, 1);
for (int i = 0; i < limit; ++i)
F[i] = F[i] * G[i];
FFT(limit, F, -1);
for (i = 0; i <= n + m; ++i)
F[i].x = (int)(F[i].x / limit + 0.5);
}
NTT
模意义下运算
//998244353 = 119*2^23 + 1; g = 3;
//1004535809 = 479*2^21 + 1; g = 3;
//2281701377 = 17*2^27 + 1; g = 3;
const int P = 998244353, G = 3, Gni = 332748118;
加const不加const 速度两个级别。原因未知。
分治FFT
cdq + FFT cdq框架
void cdq(l,r){
cdq(l,mid);
add effect of l--mid to mid+1--r
cdq(mid+1,r)
}
任意模数NTT(MTT)
设模mod
数范围m,数长度n,最大值为$nm^2$。取三个(或多个)素数乘积大于该值,再在模意义下利用中国剩余定理求解。
为防止精度爆炸,求解三项CRT时使用如下技巧:
$$Ans = kM+A = xm_3+a_3$$
$$kM = a_3-A (mod m_3)$$
$$k = (a_3-A)M^{-1} (m_3)$$
继而在mod意义下计算kM+A即可
非素数模求逆不能用快速幂
注意:使用快乘
KWT
构造FWT(A)使FWT(A@B) = FWT(A)*FWT(B)
xor
$$A@B = (A_0@B_0+A_1@B_1,A_1@B_0+A_0@B_1)$$ $$FWT(A) = (FWT(A_0+A_1),FWT(A_0-A_1))$$ $$DWT(A) = (DWT((A_0+A_1)/2),DWT((A_0-A_1)/2)))$$
and
$$A@B = (A_0@B0+A_0@B_1+A_1@B_0,A_1@B_1)$$ $$FWT(A) = (FWT(A_0+A_1),FWT(A_1))$$ $$DWT(A) = (DWT(A_0-A_1),DWT(A_1))$$
or
$$A@B = (A_0@B_0,A_0@B_1+A_1@B_0+A_1@B_1)$$ $$FWT(A) = (FWT(A_0),FWT(A_0+A_1))$$ $$DWT(A) = (DWT(A_0),DWT(A_1-A_0))$$
KMT
利用子集和变换及其逆变换,直接相乘
DPi(x) 为只考虑前x位的子集得到的变换
若DPi(x+1) = DPi(x) + DPi(x{i})
集合意义下: $$f_{S\cup{i}}^i = f_S^{i-1} + f_{S\cup{i}}^{i-1}$$
and
for (int i = 0; i < n; ++i)
for (int j = 0; j < 1 << n; j++)
if (j >> i & 1)
a[j] = md(a[j] + a[j ^ (1 << i)] * (type == 1 ? 1 : -1));
or
for (int i=0;i<n;++i)
for (int j=0;j<1<<n;j++)
if (j>>i&1)
a[j] = md(a[j]+a[j^(1<<i)]*(type==1?1:-1));
多项式求逆
要求
求n-1次多项式F关于模$x^{n}$的逆$F^{-1}$
推导
$h(x)f(x)=1 (x^n)$
$h(x)f(x)-1 = 0 (x^n)$
$h^2(x)f^2(x)-2*h(x)*f(x)+1 = 0 (x^{2n})$
$h^2(x)f(x)-2h(x)+g(x) = 0 (x^{2n})$
$g(x) = 2h(x)-h^2(x)f(x) = h(x)(2-h(x)f(x)) (x^{2n})$
算法
利用递归+FFT
也可不用递归,使用递推
多项式除法
$$A_R(x) = x^nA(\frac{1}{x})$$ 两函数满足:$A_R[i] = A[n-i]$
推导: $$F(x) = Q(x)*G(x) + R(x)$$ $$x^nF(\frac{1}{x}) = x^{n-m}Q(\frac{1}{x})*x^mG(\frac{1}{x})+ x^{n-m+1}x^{m-1}R)\frac{1}{x})$$ $$F_R(x)=Q_R(x)*G_R(x)+x^{n-m+1}*R_R(x) (mod\ x^{n-m+1})$$ $$Q_R(x) = F_R(x)*G_R^{-1}(x) (mod x^{n-m+1})$$
继而, $$R(x) = F(x) - G(x)*Q(x)$$
核心代码
//多项式除法
//O(nlogn) F(x) = Q(x)G(x) + R(x) n = m*(n-m) + (m-1)
LL G_R[MAXN],G_R_inv[MAXN],F_R[MAXN],Q_R[MAXN],G_Q[MAXN];
void poly_div(int n,int m,int mod,LL F[],LL G[],LL Q[],LL R[]){
for (int i=0;i<=m;++i) G_R[i] = G[m-i];
for (int i=n-m+1;i<=n;++i) G_R[i] = 0;
poly_inv(G_R, n-m+1, mod, G_R_inv);
for (int i=0;i<=n-m;++i) F_R[i] = F[n-i];
poly_mult(n-m, n-m, F_R, G_R_inv, Q_R);
for (int i=0;i<=n-m;++i) Q[i] = Q_R[n-m-i];
poly_mult(m,n-m,G,Q,G_Q);
for (int i=0;i<=n;++i) R[i] = md(F[i] - G_Q[i]);
}
卡常技巧
- IO优化
- inline
- 取模优化
inline int inc(int x,int v,int mod){x+=v;return x>=mod?x-mod:x;} //代替取模+
inline int dec(int x,int v,int mod){x-=v;return x<0?x+mod:x;} //代替取模-
- 前置++
- bool改char
- ()?():()快于if()else
- ,快于;
- 数组在用方括号时做了一次加法才能取地址
- 多维数组大数在前: int f[10000][1000][100]
- int 快于 long long
- STL常数可能大
- memset快
- strlen O(L)
- 位运算