Skip to Content

Introducción a la Transformada Rápida de Fourier

Tutorial

Video de YouTube (h7apO7q16V0)

Recursos
FuenteRecursoNotas
cp-algoFFT

Implementación

CSAFFT and Variations
CFadamant - Lecture Notes
CFFFT / NTT: The tough made simple Pt 1

ver también Pt 2

HechoFuenteNombreDificultadTagsSolución
YSConvolution ModFácilFFTen el módulo
Recursos
FuenteRecursoNotas
BenqFFT

Solución - Convolution Mod

Nótese que ci=j=0iajbijc_i=\sum_{j=0}^i a_jb_{i-j} es el coeficiente si tratáramos aa y bb como polinomios. Recordemos que axjbxij=abxiax^jbx^{i-j}=abx^i es el coeficiente de una multiplicación que lleva a cic_i. Así, al sumar esto, obtenemos el coeficiente de cada término del polinomio. Como este es exactamente el propósito de la FFT, podemos usar nuestra implementación favorita de FFT para resolver este problema.

Implementación

#include <bits/stdc++.h> using namespace std; using ll = long long; using db = long double; // or double, if TL is tight using str = string; // yay python! using vl = vector<ll>; using vi = vector<int>; #define tcT template <class T #define tcTU tcT, class U tcT > using V = vector<T>; tcT, size_t SZ > using AR = array<T, SZ>; tcT > using PR = pair<T, T>; // pairs #define mp make_pair #define f first #define s second #define sz(x) int((x).size()) // loops #define FOR(i, a, b) for (int i = (a); i < (b); ++i) #define F0R(i, a) FOR(i, 0, a) #define ROF(i, a, b) for (int i = (b) - 1; i >= (a); --i) #define R0F(i, a) ROF(i, 0, a) #define each(a, x) for (auto &a : x) // INPUT #define tcTUU tcT, class... U tcT > void re(T &x) { cin >> x; } tcTUU > void re(T &t, U &...u) { re(t); re(u...); } tcT > void re(V<T> &x) { each(a, x) re(a); } void setPrec() { cout << fixed << setprecision(15); } void unsyncIO() { cin.tie(0)->sync_with_stdio(0); } void setIO() { unsyncIO(); setPrec(); } #define rep(i, a, b) for (int i = a; i < (b); ++i) typedef pair<int, int> pii; /** * Author: Ludo Pulles, chilli, Simon Lindholm * Date: 2019-01-09 * License: CC0 * Source: http://neerc.ifmo.ru/trains/toulouse/2017/fft2.pdf (do read, it's excellent) Accuracy bound from http://www.daemonology.net/papers/fft.pdf * Description: fft(a) computes $\hat f(k) = \sum_x a[x] \exp(2\pi i \cdot k x / N)$ for all $k$. N must be a power of 2. Useful for convolution: \texttt{conv(a, b) = c}, where $c[x] = \sum a[i]b[x-i]$. For convolution of complex numbers or more than two vectors: FFT, multiply pointwise, divide by n, reverse(start+1, end), FFT back. Rounding is safe if $(\sum a_i^2 + \sum b_i^2)\log_2{N} < 9\cdot10^{14}$ (in practice $10^{16}$; higher for random inputs). Otherwise, use NTT/FFTMod. * Time: O(N \log N) with $N = |A|+|B|$ ($\tilde 1s$ for $N=2^{22}$) * Status: somewhat tested * Details: An in-depth examination of precision for both FFT and FFTMod can be found * here (https://github.com/simonlindholm/fft-precision/blob/master/fft-precision.md) */ typedef complex<double> C; void fft(vector<C> &a) { int n = sz(a), L = 31 - __builtin_clz(n); static vector<complex<long double>> R(2, 1); static vector<C> rt(2, 1); // (^ 10% faster if double) for (static int k = 2; k < n; k *= 2) { R.resize(n); rt.resize(n); auto x = polar(1.0L, acos(-1.0L) / k); rep(i, k, 2 * k) rt[i] = R[i] = i & 1 ? R[i / 2] * x : R[i / 2]; } vi rev(n); rep(i, 0, n) rev[i] = (rev[i / 2] | (i & 1) << L) / 2; rep(i, 0, n) if (i < rev[i]) swap(a[i], a[rev[i]]); for (int k = 1; k < n; k *= 2) for (int i = 0; i < n; i += 2 * k) rep(j, 0, k) { // C z = rt[j+k] * a[i+j+k]; // (25% faster if hand-rolled) /// // include-line auto x = (double *)&rt[j + k], y = (double *)&a[i + j + k]; /// exclude-line C z(x[0] * y[0] - x[1] * y[1], x[0] * y[1] + x[1] * y[0]); /// exclude-line a[i + j + k] = a[i + j] - z; a[i + j] += z; } } typedef vector<ll> vl; template <int M> vl convMod(const vl &a, const vl &b) { if (a.empty() || b.empty()) return {}; vl res(sz(a) + sz(b) - 1); int B = 32 - __builtin_clz(sz(res)), n = 1 << B, cut = int(sqrt(M)); vector<C> L(n), R(n), outs(n), outl(n); rep(i, 0, sz(a)) L[i] = C((int)a[i] / cut, (int)a[i] % cut); rep(i, 0, sz(b)) R[i] = C((int)b[i] / cut, (int)b[i] % cut); fft(L), fft(R); rep(i, 0, n) { int j = -i & (n - 1); outl[j] = (L[i] + conj(L[j])) * R[i] / (2.0 * n); outs[j] = (L[i] - conj(L[j])) * R[i] / (2.0 * n) / 1i; } fft(outl), fft(outs); rep(i, 0, sz(res)) { ll av = ll(real(outl[i]) + .5), cv = ll(imag(outs[i]) + .5); ll bv = ll(imag(outl[i]) + .5) + ll(real(outs[i]) + .5); res[i] = ((av % M * cut + bv) % M * cut + cv) % M; } return res; } int main() { setIO(); int n, m; re(n, m); vl A(n); re(A); vl B(m); re(B); auto ret = convMod<998244353>(A, B); // just change this number for mod F0R(i, sz(ret)) { cout << ret[i]; if (i != sz(ret) - 1) cout << " "; } }
HechoFuenteNombreDificultadTagsSolución
YSConvolution Mod 10^9+7NormalFFTen el módulo
Recursos
FuenteRecursoNotas
BenqFFTMod

NTT con tres módulos distintos

Solución - Convolution Mod 109+710^9+7

Nótese que este es exactamente el mismo problema que Convolution Mod, así que basta con cambiar el módulo.

Implementación

#include <bits/stdc++.h> using namespace std; using ll = long long; using db = long double; // or double, if TL is tight using str = string; // yay python! using vl = vector<ll>; using vi = vector<int>; #define tcT template <class T #define tcTU tcT, class U tcT > using V = vector<T>; tcT, size_t SZ > using AR = array<T, SZ>; tcT > using PR = pair<T, T>; // pairs #define mp make_pair #define f first #define s second #define sz(x) int((x).size()) // loops #define FOR(i, a, b) for (int i = (a); i < (b); ++i) #define F0R(i, a) FOR(i, 0, a) #define ROF(i, a, b) for (int i = (b) - 1; i >= (a); --i) #define R0F(i, a) ROF(i, 0, a) #define each(a, x) for (auto &a : x) // INPUT #define tcTUU tcT, class... U tcT > void re(T &x) { cin >> x; } tcTUU > void re(T &t, U &...u) { re(t); re(u...); } tcT > void re(V<T> &x) { each(a, x) re(a); } void setPrec() { cout << fixed << setprecision(15); } void unsyncIO() { cin.tie(0)->sync_with_stdio(0); } void setIO() { unsyncIO(); setPrec(); } #define rep(i, a, b) for (int i = a; i < (b); ++i) typedef pair<int, int> pii; /** * Author: Ludo Pulles, chilli, Simon Lindholm * Date: 2019-01-09 * License: CC0 * Source: http://neerc.ifmo.ru/trains/toulouse/2017/fft2.pdf (do read, it's excellent) Accuracy bound from http://www.daemonology.net/papers/fft.pdf * Description: fft(a) computes $\hat f(k) = \sum_x a[x] \exp(2\pi i \cdot k x / N)$ for all $k$. N must be a power of 2. Useful for convolution: \texttt{conv(a, b) = c}, where $c[x] = \sum a[i]b[x-i]$. For convolution of complex numbers or more than two vectors: FFT, multiply pointwise, divide by n, reverse(start+1, end), FFT back. Rounding is safe if $(\sum a_i^2 + \sum b_i^2)\log_2{N} < 9\cdot10^{14}$ (in practice $10^{16}$; higher for random inputs). Otherwise, use NTT/FFTMod. * Time: O(N \log N) with $N = |A|+|B|$ ($\tilde 1s$ for $N=2^{22}$) * Status: somewhat tested * Details: An in-depth examination of precision for both FFT and FFTMod can be found * here (https://github.com/simonlindholm/fft-precision/blob/master/fft-precision.md) */ typedef complex<double> C; void fft(vector<C> &a) { int n = sz(a), L = 31 - __builtin_clz(n); static vector<complex<long double>> R(2, 1); static vector<C> rt(2, 1); // (^ 10% faster if double) for (static int k = 2; k < n; k *= 2) { R.resize(n); rt.resize(n); auto x = polar(1.0L, acos(-1.0L) / k); rep(i, k, 2 * k) rt[i] = R[i] = i & 1 ? R[i / 2] * x : R[i / 2]; } vi rev(n); rep(i, 0, n) rev[i] = (rev[i / 2] | (i & 1) << L) / 2; rep(i, 0, n) if (i < rev[i]) swap(a[i], a[rev[i]]); for (int k = 1; k < n; k *= 2) for (int i = 0; i < n; i += 2 * k) rep(j, 0, k) { // C z = rt[j+k] * a[i+j+k]; // (25% faster if hand-rolled) /// // include-line auto x = (double *)&rt[j + k], y = (double *)&a[i + j + k]; /// exclude-line C z(x[0] * y[0] - x[1] * y[1], x[0] * y[1] + x[1] * y[0]); /// exclude-line a[i + j + k] = a[i + j] - z; a[i + j] += z; } } typedef vector<ll> vl; template <int M> vl convMod(const vl &a, const vl &b) { if (a.empty() || b.empty()) return {}; vl res(sz(a) + sz(b) - 1); int B = 32 - __builtin_clz(sz(res)), n = 1 << B, cut = int(sqrt(M)); vector<C> L(n), R(n), outs(n), outl(n); rep(i, 0, sz(a)) L[i] = C((int)a[i] / cut, (int)a[i] % cut); rep(i, 0, sz(b)) R[i] = C((int)b[i] / cut, (int)b[i] % cut); fft(L), fft(R); rep(i, 0, n) { int j = -i & (n - 1); outl[j] = (L[i] + conj(L[j])) * R[i] / (2.0 * n); outs[j] = (L[i] - conj(L[j])) * R[i] / (2.0 * n) / 1i; } fft(outl), fft(outs); rep(i, 0, sz(res)) { ll av = ll(real(outl[i]) + .5), cv = ll(imag(outs[i]) + .5); ll bv = ll(imag(outl[i]) + .5) + ll(real(outs[i]) + .5); res[i] = ((av % M * cut + bv) % M * cut + cv) % M; } return res; } int main() { setIO(); int n, m; re(n, m); vl A(n); re(A); vl B(m); re(B); auto ret = convMod<1000000007>(A, B); // just change this number for mod F0R(i, sz(ret)) { cout << ret[i]; if (i != sz(ret) - 1) cout << " "; } }

Nota - FFT Killer

La “multiplicación con módulo arbitrario” descrita en cp-algo requiere long double para pasar.

Problemas

HechoFuenteNombreDificultadTagsSolución
POI2018 - PolynomialFácilSolución
KattisK-InversionsFácilSolución
KattisMatchingsNormalSolución
KattisAlien CodebreakingMuy difícil

Sobre un árbol

HechoFuenteNombreDificultadTagsSolución
YSFrequency Table of Tree DistanceDifícilCentroid, FFT
Back to SchoolBig IntegerMuy difícilCentroid, FFT