This documentation is automatically generated by online-judge-tools/verification-helper
#include "src/FPS/FFT_fast.hpp"
より高速な FFT.4 進でやるとより高速になるが,長すぎるので妥協
void fft(vector<mm>& a)
:長さが $n$ : $2$ べきの数列 $a$ の離散フーリエ変換を行う
fft()
後の配列は bit reversal 順になっていることに注意.例えば $n = 16$ のとき,$(0101)_2 = 5$ の bit 順を反転させると $(1010)_2 = 10$ であるから,$f(\omega^5)$ の値は $a[10]$ に入っている.void ifft(vector<mm>& a)
:fft()
の逆変換を行うvector<mm> conv(vector<mm> a, vector<mm> b)
:数列 $a, b$ の畳み込みを行う
$O(n \log n)$ 時間
fft()
, ifft()
の入力は長さが 2 べきであること// modint を u32 にして加減算を真面目にやると速い
mm g = 3; // 原始根
void fft(vector<mm>& a) {
ll n = sz(a), lg = __lg(n);
static auto z = [] {
vector<mm> z(30);
mm s = 1;
rep(i, 2, 32) {
z[i - 2] = s * g.pow(mod >> i);
s *= g.inv().pow(mod >> i);
}
return z;
}();
rep(l, 0, lg) {
ll w = 1 << (lg - l - 1);
mm s = 1;
rep(k, 0, 1 << l) {
ll o = k << (lg - l);
rep(i, o, o + w) {
mm x = a[i], y = a[i + w] * s;
a[i] = x + y;
a[i + w] = x - y;
}
s *= z[countr_zero<uint64_t>(~k)];
}
}
}
// コピペ
void ifft(vector<mm>& a) {
ll n = sz(a), lg = __lg(n);
static auto z = [] {
vector<mm> z(30);
mm s = 1;
rep(i, 2, 32) { // g を逆数に
z[i - 2] = s * g.inv().pow(mod >> i);
s *= g.pow(mod >> i);
}
return z;
}();
for(ll l = lg; l--;) { // 逆順に
ll w = 1 << (lg - l - 1);
mm s = 1;
rep(k, 0, 1 << l) {
ll o = k << (lg - l);
rep(i, o, o + w) {
mm x = a[i], y = a[i + w]; // *s を下に移動
a[i] = x + y;
a[i + w] = (x - y) * s;
}
s *= z[countr_zero<uint64_t>(~k)];
}
}
}
vector<mm> conv(vector<mm> a, vector<mm> b) {
if(a.empty() || b.empty()) return {};
size_t s = sz(a) + sz(b) - 1, n = bit_ceil(s);
// if(min(sz(a), sz(b)) <= 60) 愚直に掛け算
a.resize(n);
b.resize(n);
fft(a);
fft(b);
mm inv = mm(n).inv();
rep(i, 0, n) a[i] *= b[i] * inv;
ifft(a);
a.resize(s);
return a;
}
#line 1 "src/FPS/FFT_fast.hpp"
// modint を u32 にして加減算を真面目にやると速い
mm g = 3; // 原始根
void fft(vector<mm>& a) {
ll n = sz(a), lg = __lg(n);
static auto z = [] {
vector<mm> z(30);
mm s = 1;
rep(i, 2, 32) {
z[i - 2] = s * g.pow(mod >> i);
s *= g.inv().pow(mod >> i);
}
return z;
}();
rep(l, 0, lg) {
ll w = 1 << (lg - l - 1);
mm s = 1;
rep(k, 0, 1 << l) {
ll o = k << (lg - l);
rep(i, o, o + w) {
mm x = a[i], y = a[i + w] * s;
a[i] = x + y;
a[i + w] = x - y;
}
s *= z[countr_zero<uint64_t>(~k)];
}
}
}
// コピペ
void ifft(vector<mm>& a) {
ll n = sz(a), lg = __lg(n);
static auto z = [] {
vector<mm> z(30);
mm s = 1;
rep(i, 2, 32) { // g を逆数に
z[i - 2] = s * g.inv().pow(mod >> i);
s *= g.pow(mod >> i);
}
return z;
}();
for(ll l = lg; l--;) { // 逆順に
ll w = 1 << (lg - l - 1);
mm s = 1;
rep(k, 0, 1 << l) {
ll o = k << (lg - l);
rep(i, o, o + w) {
mm x = a[i], y = a[i + w]; // *s を下に移動
a[i] = x + y;
a[i + w] = (x - y) * s;
}
s *= z[countr_zero<uint64_t>(~k)];
}
}
}
vector<mm> conv(vector<mm> a, vector<mm> b) {
if(a.empty() || b.empty()) return {};
size_t s = sz(a) + sz(b) - 1, n = bit_ceil(s);
// if(min(sz(a), sz(b)) <= 60) 愚直に掛け算
a.resize(n);
b.resize(n);
fft(a);
fft(b);
mm inv = mm(n).inv();
rep(i, 0, n) a[i] *= b[i] * inv;
ifft(a);
a.resize(s);
return a;
}