Introducción a la Transformada Rápida de Fourier
Tutorial
Video de YouTube (h7apO7q16V0)
| Fuente | Recurso | Notas |
|---|---|---|
| cp-algo | FFT | Implementación |
| CSA | FFT and Variations | |
| CF | adamant - Lecture Notes | |
| CF | FFT / NTT: The tough made simple Pt 1 | ver también Pt 2 |
| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| YS | Convolution Mod | Fácil | FFT | en el módulo |
| Fuente | Recurso | Notas |
|---|---|---|
| Benq | FFT |
Solución - Convolution Mod
Nótese que es el coeficiente si tratáramos y como polinomios. Recordemos que es el coeficiente de una multiplicación que lleva a . 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 << " ";
}
}| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| YS | Convolution Mod 10^9+7 | Normal | FFT | en el módulo |
| Fuente | Recurso | Notas |
|---|---|---|
| Benq | FFTMod | NTT con tres módulos distintos |
Solución - Convolution Mod
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
| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| POI | 2018 - Polynomial | Fácil | Solución | ||
| Kattis | K-Inversions | Fácil | Solución | ||
| Kattis | Matchings | Normal | Solución | ||
| Kattis | Alien Codebreaking | Muy difícil | — |
Sobre un árbol
| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| YS | Frequency Table of Tree Distance | Difícil | Centroid, FFT | — | |
| Back to School | Big Integer | Muy difícil | Centroid, FFT | — |