拉格朗日插值
给出 n n n 个点 ( x i , y i ) (x_i,y_i) ( x i , y i ) ,求出过这 n n n 个点的 n − 1 n-1 n − 1 次多项式 L ( x ) L(x) L ( x ) 。
拉格朗日插值多项式:
L ( x ) = ∑ i = 0 n ∏ j ≠ i ( x − x j ) ∏ j ≠ i ( x i − x j ) ⋅ y i L(x)=\sum_{i=0}^n\frac{\prod_{j\ne i}(x-x_j)}{\prod_{j\ne i}(x_i-x_j)}\cdot y_i
L ( x ) = i = 0 ∑ n ∏ j = i ( x i − x j ) ∏ j = i ( x − x j ) ⋅ y i
时间复杂度 O ( n 2 ) O(n^2) O ( n 2 ) 。
快速傅里叶变换 FFT
单位根
定义:ω n = e 2 π i n = cos ( 2 π n ) + i ⋅ sin ( 2 π n ) \omega_n=e^{\frac{2\pi i}n}=\cos(\frac{2\pi}n)+i\cdot\sin(\frac{2\pi}n) ω n = e n 2 π i = cos ( n 2 π ) + i ⋅ sin ( n 2 π ) 。
两个复数相乘,模长相乘,幅角相加。
性质:
ω 2 n n + k = − ω 2 n k \omega_{2n}^{n+k}=-\omega_{2n}^k ω 2 n n + k = − ω 2 n k
ω 2 n 2 n + k = ω 2 n k \omega_{2n}^{2n+k}=\omega_{2n}^k ω 2 n 2 n + k = ω 2 n k
ω m n m k = ω n k \omega_{mn}^{mk}=\omega_n^k ω m n m k = ω n k
点值序列
一个 n n n 次多项式可以由 n + 1 n+1 n + 1 个点确定。
而将 f × g f\times g f × g 可以由 f f f 的 2 n + 1 2n+1 2 n + 1 个点和 g g g 的 2 n + 1 2n+1 2 n + 1 个点对应相乘得到。
快速系数转点值 DFT
f ( ω n k ) = a 0 + a 1 ω n k + a 2 ω n 2 k + ⋯ + a n − 1 ω n ( n − 1 ) k = a 0 + a 2 ω n 2 k + ⋯ + a n − 2 ω n ( n − 2 ) k + a 1 ω n k + a 3 ω n 3 k + ⋯ + a n − 1 ω n ( n − 1 ) k = a 0 + a 2 ω n / 2 2 k + ⋯ + a n − 2 ω n / 2 ( n − 2 ) k + ω n k ( a 1 + a 3 ω n / 2 2 k + ⋯ + a n − 1 ω n / 2 ( n − 2 ) k ) \begin{aligned}
f\left(\omega_n^k\right)&=a_0+a_1\omega_n^k+a_2\omega_n^{2k}+\cdots+a_{n-1}\omega_n^{(n-1)k}\\
&=a_0+a_2\omega_n^{2k}+\cdots+a_{n-2}\omega_n^{(n-2)k}+a_1\omega_n^k+a_3\omega_n^{3k}+\cdots+a_{n-1}\omega_n^{(n-1)k}\\
&=a_0+a_2\omega_{n/2}^{2k}+\cdots+a_{n-2}\omega_{n/2}^{(n-2)k}+\omega_n^k\left(a_1+a_3\omega_{n/2}^{2k}+\cdots+a_{n-1}\omega_{n/2}^{(n-2)k}\right)\\
\end{aligned}
f ( ω n k ) = a 0 + a 1 ω n k + a 2 ω n 2 k + ⋯ + a n − 1 ω n ( n − 1 ) k = a 0 + a 2 ω n 2 k + ⋯ + a n − 2 ω n ( n − 2 ) k + a 1 ω n k + a 3 ω n 3 k + ⋯ + a n − 1 ω n ( n − 1 ) k = a 0 + a 2 ω n / 2 2 k + ⋯ + a n − 2 ω n / 2 ( n − 2 ) k + ω n k ( a 1 + a 3 ω n / 2 2 k + ⋯ + a n − 1 ω n / 2 ( n − 2 ) k )
故可以拆成奇偶两部分,偶在左,奇在右。将两部分分别处理完后,可以合并为:
{ f ( ω n k ) = g 0 ( ω n / 2 k ) + ω n k ⋅ g 1 ( ω n / 2 k ) f ( ω n k + ( n / 2 ) ) = g 0 ( ω n / 2 k ) − ω n k ⋅ g 1 ( ω n / 2 k ) \begin{cases}
f\left(\omega_n^k\right)&=g_0\left(\omega_{n/2}^k\right)+\omega_n^k\cdot g_1\left(\omega_{n/2}^k\right)\\
f\left(\omega_n^{k+(n/2)}\right)&=g_0\left(\omega_{n/2}^k\right)-\omega_n^k\cdot g_1\left(\omega_{n/2}^k\right)
\end{cases}
⎩ ⎪ ⎨ ⎪ ⎧ f ( ω n k ) f ( ω n k + ( n / 2 ) ) = g 0 ( ω n / 2 k ) + ω n k ⋅ g 1 ( ω n / 2 k ) = g 0 ( ω n / 2 k ) − ω n k ⋅ g 1 ( ω n / 2 k )
第二个式子的符号是 + ( n / 2 ) +(n/2) + ( n / 2 ) 来的。
快速点值转系数 IDFT
将上式 ω n k \omega_n^k ω n k 前的符号取反一下即可。最后记得再 ÷ n \div n ÷ n 。
非递归版本
考虑位逆序置换(蝴蝶变换),0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 0,1,2,3,4,5,6,7 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 会变换成 0 , 4 , 2 , 6 , 1 , 5 , 3 , 7 0,4,2,6,1,5,3,7 0 , 4 , 2 , 6 , 1 , 5 , 3 , 7 。即翻转 i i i 的二进制表示后即为最终位置的下标。
定义 c ( i ) c(i) c ( i ) 表示 i i i 的翻转二进制表示。它等于 c ( ⌊ i 2 ⌋ ) c\left(\lfloor\frac i2\rfloor\right) c ( ⌊ 2 i ⌋ ) 右移一位后补上最高位。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 typedef double db;typedef std::complex<db> comp;const db PI = acos (-1.0 );const int N = 3e6 + 7 ;void DFT (comp* f, int n, int rev) { static int c[N]; static comp tmp[N]; for (int i = 0 ; i < n; i++) tmp[i] = f[i]; for (int i = 0 ; i < n; i++) f[i] = tmp[c[i] = (c[i / 2 ] / 2 ) | ((i & 1 ) ? (n / 2 ) : 0 )]; for (int l = 2 ; l <= n; l *= 2 ) { comp step (cos(2 * PI / l), sin(2 * PI / l * rev)) ; for (int i = 0 ; i + l <= n; i += l) { comp cur (1 , 0 ) ; for (int k = i; k < i + l / 2 ; k++, cur *= step) tmp[k] = f[k] + cur * f[k + l / 2 ], tmp[k + l / 2 ] = f[k] - cur * f[k + l / 2 ]; } for (int i = 0 ; i < n; i++) f[i] = tmp[i]; } }
快速数论变换 NTT [UNFINISHED]
原根有着和单位根相似的性质。要求模数形如 q × 2 k + 1 q\times2^k+1 q × 2 k + 1 ,NTT 模数表 。
快速莫比乌斯变换 FMT
求 c i = ∑ j and k = i a j b k c_i=\sum\limits_{j~\text{and}~k=i}a_jb_k c i = j and k = i ∑ a j b k 。
对于集合幂级数 f f f ,定义 f ′ f' f ′ :f S ′ = ∑ S ⊆ T f T f'_S=\sum_{S\subseteq T}f_T f S ′ = ∑ S ⊆ T f T 。即 S S S 所有超集之和。等价于高维后缀和。
于是上式变为了:
c S ′ = ∑ S ⊆ T ∑ P ∩ Q = T a P ⋅ b Q = ∑ S ⊆ P a P ∑ S ⊆ Q b Q = a p ′ ⋅ b Q ′ \begin{aligned}
c'_S&=\sum_{S\subseteq T}\sum_{P\cap Q=T}a_P\cdot b_Q\\
&=\sum_{S\subseteq P}a_P\sum_{S\subseteq Q}b_Q\\
&=a'_p\cdot b'_Q
\end{aligned}
c S ′ = S ⊆ T ∑ P ∩ Q = T ∑ a P ⋅ b Q = S ⊆ P ∑ a P S ⊆ Q ∑ b Q = a p ′ ⋅ b Q ′
求出 c S ′ c'_S c S ′ 后,高维差分得到 c S c_S c S 。
求 c i = ∑ j or k = i a j b k c_i=\sum\limits_{j~\text{or}~k=i}a_jb_k c i = j or k = i ∑ a j b k ,把后缀和改为前缀和即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void Or (int n, LL* a, bool rev) { for (int i = 0 ; i < n; i++) for (int j = 0 ; j < (1 << n); j++) if (j & (1 << i)) { a[j] += (rev ? mod - a[j - (1 << i)] : a[j - (1 << i)]); if (a[j] >= mod) a[j] -= mod; } } void And (int n, LL* a, bool rev) { for (int i = 0 ; i < n; i++) for (int j = (1 << n) - 1 ; ~j; j--) if (!(j & (1 << i))) { a[j] += (rev ? mod - a[j | (1 << i)] : a[j | (1 << i)]); if (a[j] >= mod) a[j] -= mod; } }
快速沃尔什变换 FWT
求 c i = ∑ j xor k = i a j b k c_i=\sum\limits_{j~\text{xor}~k=i}a_jb_k c i = j xor k = i ∑ a j b k 。
对称差:A Δ B = { x ∣ x ∈ A ∪ B , x ∉ A ∩ B } A\Delta B=\{x|x\in A\cup B,x\not\in A\cap B\} A Δ B = { x ∣ x ∈ A ∪ B , x ∈ A ∩ B } 。
对于集合幂级数 f f f ,定义 f ′ f' f ′ :f S ′ = ∑ T f T ⋅ ( − 1 ) ∣ S ∩ T ∣ f'_S=\sum_Tf_T\cdot(-1)^{|S\cap T|} f S ′ = ∑ T f T ⋅ ( − 1 ) ∣ S ∩ T ∣ 。
推一推:
c S ′ = ∑ T ( − 1 ) ∣ S ∩ T ∣ ∑ P Δ Q = S a P ⋅ b Q = ∑ P a P ∑ Q b Q ⋅ ( − 1 ) ∣ ( P Δ Q ) ∩ T ∣ = ∑ P a P ⋅ ( − 1 ) ∣ P ∩ T ∣ ∑ Q b Q ⋅ ( − 1 ) ∣ Q ∩ T ∣ = a P ′ ⋅ b Q ′ \begin{aligned}
c'_S&=\sum_T(-1)^{|S\cap T|}\sum_{P\Delta Q=S}a_P\cdot b_Q\\
&=\sum_Pa_P\sum_Qb_Q\cdot(-1)^{|(P\Delta Q)\cap T|}\\
&=\sum_Pa_P\cdot(-1)^{|P\cap T|}\sum_Qb_Q\cdot(-1)^{|Q\cap T|}\\
&=a'_P\cdot b'_Q
\end{aligned}
c S ′ = T ∑ ( − 1 ) ∣ S ∩ T ∣ P Δ Q = S ∑ a P ⋅ b Q = P ∑ a P Q ∑ b Q ⋅ ( − 1 ) ∣ ( P Δ Q ) ∩ T ∣ = P ∑ a P ⋅ ( − 1 ) ∣ P ∩ T ∣ Q ∑ b Q ⋅ ( − 1 ) ∣ Q ∩ T ∣ = a P ′ ⋅ b Q ′
问题变成了如何求 a S ′ a'_S a S ′ 和 b S ′ b'_S b S ′ 。
类似 FFT 地,我们按照是否含有第 n − 1 n-1 n − 1 个元素,将所有下标分为 0 ∼ 2 n − 1 − 1 0\sim 2^{n-1}-1 0 ∼ 2 n − 1 − 1 和 2 n − 1 ∼ 2 n − 1 2^{n-1}\sim2^n-1 2 n − 1 ∼ 2 n − 1 。假设左部递归出来为 g 0 , S = ∑ 0 ≤ T < 2 n − 1 f T ⋅ ( − 1 ) ∣ S ∩ T ∣ g_{0,S}=\sum_{0\le T<2^{n-1}}f_T\cdot(-1)^{|S\cap T|} g 0 , S = ∑ 0 ≤ T < 2 n − 1 f T ⋅ ( − 1 ) ∣ S ∩ T ∣ ,右部递归出来为 g 1 , S = ∑ 2 n − 1 ≤ T < 2 n f T ⋅ ( − 1 ) ∣ S ∩ T ∣ − 1 g_{1,S}=\sum_{2^{n-1}\le T<2^n}f_T\cdot(-1)^{|S\cap T|-1} g 1 , S = ∑ 2 n − 1 ≤ T < 2 n f T ⋅ ( − 1 ) ∣ S ∩ T ∣ − 1 。− 1 -1 − 1 是因为去掉了第 n − 1 n-1 n − 1 个元素。
推一下左部的表达式:
f 0 , S ′ = ∑ 0 ≤ T < 2 n f T ⋅ ( − 1 ) ∣ S ∩ T ∣ = ∑ 0 ≤ T < 2 n − 1 f T ⋅ ( − 1 ) ∣ S ∩ T ∣ + ∑ 2 n − 1 ≤ T < 2 n f T ⋅ ( − 1 ) ∣ S ∩ T ∣ = ∑ 0 ≤ T < 2 n − 1 f T ⋅ ( − 1 ) ∣ S ∩ T ∣ + ∑ 2 n − 1 ≤ T < 2 n f T ⋅ ( − 1 ) ∣ ( S ∪ { n + 1 } ) ∩ T ∣ − 1 = g 0 , S + g 1 , S \begin{aligned}
f'_{0,S}&=\sum_{0\le T<2^n}f_T\cdot(-1)^{|S\cap T|}\\
&=\sum_{0\le T<2^{n-1}}f_T\cdot(-1)^{|S\cap T|}+\sum_{2^{n-1}\le T<2^n}f_T\cdot(-1)^{|S\cap T|}\\
&=\sum_{0\le T<2^{n-1}}f_T\cdot(-1)^{|S\cap T|}+\sum_{2^{n-1}\le T<2^n}f_T\cdot(-1)^{|(S\cup\{n+1\})\cap T|-1}\\
&=g_{0,S}+g_{1,S}
\end{aligned}
f 0 , S ′ = 0 ≤ T < 2 n ∑ f T ⋅ ( − 1 ) ∣ S ∩ T ∣ = 0 ≤ T < 2 n − 1 ∑ f T ⋅ ( − 1 ) ∣ S ∩ T ∣ + 2 n − 1 ≤ T < 2 n ∑ f T ⋅ ( − 1 ) ∣ S ∩ T ∣ = 0 ≤ T < 2 n − 1 ∑ f T ⋅ ( − 1 ) ∣ S ∩ T ∣ + 2 n − 1 ≤ T < 2 n ∑ f T ⋅ ( − 1 ) ∣ ( S ∪ { n + 1 } ) ∩ T ∣ − 1 = g 0 , S + g 1 , S
同理,右部的表达式:
f 1 , S ∪ { n − 1 } ′ = ∑ 0 ≤ T < 2 n f T ⋅ ( − 1 ) ∣ ( S ∪ { n + 1 } ) ∩ T ∣ = ∑ 0 ≤ T < 2 n − 1 f T ⋅ ( − 1 ) ∣ S ∩ T ∣ + ∑ 2 n − 1 ≤ T < 2 n f T ⋅ ( − 1 ) ∣ ( S ∪ { n + 1 } ) ∩ T ∣ = g 0 , S − g 1 , S \begin{aligned}
f'_{1,S\cup\{n-1\}}&=\sum_{0\le T<2^n}f_T\cdot(-1)^{|(S\cup\{n+1\})\cap T|}\\
&=\sum_{0\le T<2^{n-1}}f_T\cdot(-1)^{|S\cap T|}+\sum_{2^{n-1}\le T<2^n}f_T\cdot(-1)^{|(S\cup\{n+1\})\cap T|}\\
&=g_{0,S}-g_{1,S}
\end{aligned}
f 1 , S ∪ { n − 1 } ′ = 0 ≤ T < 2 n ∑ f T ⋅ ( − 1 ) ∣ ( S ∪ { n + 1 } ) ∩ T ∣ = 0 ≤ T < 2 n − 1 ∑ f T ⋅ ( − 1 ) ∣ S ∩ T ∣ + 2 n − 1 ≤ T < 2 n ∑ f T ⋅ ( − 1 ) ∣ ( S ∪ { n + 1 } ) ∩ T ∣ = g 0 , S − g 1 , S
逆变换记得每层 ÷ 2 \div2 ÷ 2 。
1 2 3 4 5 6 7 8 9 10 11 void Xor (int n, LL* f, bool rev) { for (int l = 2 ; l <= (1 << n); l *= 2 ) for (int i = 0 ; i < (1 << n); i += l) for (int k = 0 ; k < l / 2 ; k++) { LL u = f[i + k], v = f[i + k + l / 2 ]; f[i + k] = u + v, f[i + k + l / 2 ] = u - v + mod; if (f[i + k] >= mod) f[i + k] -= mod; if (f[i + k + l / 2 ] >= mod) f[i + k + l / 2 ] -= mod; if (rev) (f[i + k] *= inv2) %= mod, (f[i + k + l / 2 ] *= inv2) %= mod; } }