幾周前搞了搞……有點時間簡要整理一下,諸多不足之處還請指出。
有哪些需要理解的地方?
- 點值表示:對于多項式 \(A(x)\),把 \(n\) 個不同的 \(x\) 代入,會得出 \(n\) 個不同的 \(y\),在坐標系內就是 \(n\) 個不同的點,那么這 \(n\) 個點唯一確定該多項式
- 為什么引入單位根 \(\omega\) 作為變量 \(x\):若代入一些 \(x\) ,使每個 \(x\) 的若干次方等于 \(1\),就不用做全部的次方運算了
- 單位根的性質:于是可以分治實現 \(FFT\),復雜度降至 \(O(n log n)\)
- 共軛復數:復數 \(z=a+bi\) 的共軛復數為 \(a?bi\)(虛部取反)。一個多項式在分治的過程中乘上單位根的共軛復數,分治完的每一項除以 \(n\) 即為原多項式的每一項系數
- 初始化序列反轉二進制數 : \(r[i] = (r[i >> 1] >> 1) | ((i \& 1) << (l - 1))\)
- 利用二進制序列優化 \(FFT\):每個位置分治后的最終位置為其二進制翻轉后得到的位置
算法的步驟?
- 輸入,將向量長度轉化成二的冪次
- 初始化序列反轉二進制數 : \(r[i] = (r[i >> 1] >> 1) | ((i \& 1) << (l - 1))\)
- 利用 \(FFT\) 將多項式轉化為點值表示,如下:
- 利用處理出的二進制求出待處理序列
- 分治,每次找序列的一半,利用單位根計算(乘上 \(k\) 次冪),后對稱到另一半
- 將點值表示相乘,利用 \(IFFT\) 將其轉換回系數表達式,輸出
- 多項式在分治的過程中乘上單位根的共軛復數,分治完的每一項除 \(n\) 即為原多項式的每一項系數
\(NTT\) 其實就是在模意義下做了些許改變,十分類似。 - \(NTT\) 模數:原根為 \(3\),有 \(469762049\),\(998244353\),\(1004535809\)
- 原根在此特殊意義下代替了單位根,除法相應地變成逆元的乘法運算
- 在模數并不是 \(NTT\) 模數時,采用 \(MTT\) 或三模數 \(NTT\) + \(CRT\)
\(FFT\) 和 \(FNT\) 板子?
// Fast_fourier_transform
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;const double pi = acos(-1.0);
const int maxn = 5000000 + 10;
int n, m, len, limit, rado, r[maxn];struct Complex {double x, y;Complex(double tx = 0, double ty = 0) { x = tx, y = ty; }
} f_1[maxn], f_2[maxn];inline Complex operator + (const Complex &a, const Complex &b) { return Complex(a.x + b.x, a.y + b.y); }
inline Complex operator - (const Complex &a, const Complex &b) { return Complex(a.x - b.x, a.y - b.y); }
inline Complex operator * (const Complex &a, const Complex &b) { return Complex(a.x * b.x - a.y * b.y, a.x * b.y + b.x * a.y); }inline int read() {register char ch = 0; register int w = 0, x = 0;while( !isdigit(ch) ) w |= (ch == '-'), ch = getchar();while( isdigit(ch) ) x = (x * 10) + (ch ^ 48), ch = getchar();return w ? -x : x;
}inline void Fast_fourior_transform(Complex *a, int type) {for(int i = 0; i < limit; ++i) if( i < r[i] ) swap(a[i], a[r[i]]);for(int mid = 1; mid < limit; mid = mid << 1) {Complex Base_w = Complex(cos(pi / mid), type * sin(pi / mid));for(int len = mid << 1, l = 0; l < limit; l = l + len) {Complex w = Complex(1, 0);for(int k = 0; k < mid; ++k, w = w * Base_w) {Complex tmp_1 = a[l + k], tmp_2 = w * a[l + mid + k];a[l + k] = tmp_1 + tmp_2, a[l + mid + k] = tmp_1 - tmp_2;}}}
}int main(int argc, char const *argv[])
{freopen("..\\nanjolno.in", "r", stdin);freopen("..\\nanjolno.out", "w", stdout);scanf("%d%d", &n, &m);for(int i = 0; i <= n; ++i) f_1[i].x = read();for(int i = 0; i <= m; ++i) f_2[i].x = read();len = n + m, limit = 1, rado = 0;while( limit <= len ) limit = limit << 1, ++rado;for(int i = 0; i < limit; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (rado - 1));Fast_fourior_transform(f_1, 1);Fast_fourior_transform(f_2, 1);for(int i = 0; i < limit; ++i) f_1[i] = f_1[i] * f_2[i];Fast_fourior_transform(f_1, -1);for(int i = 0; i <= len; ++i) printf("%d\n", (int)(f_1[i].x / limit + 0.1));fclose(stdin), fclose(stdout);return 0;
} // Fast_number_theory_transform
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;const int mod = 998244353;
const int maxn = 5000000 + 10;
int n, m, len, limit, rado, f_1[maxn], f_2[maxn], r[maxn];inline int read() {register char ch = 0; register int w = 0, x = 0;while( !isdigit(ch) ) w |= (ch == '-'), ch = getchar();while( isdigit(ch) ) x = (x * 10) + (ch ^ 48), ch = getchar();return w ? -x : x;
}inline int Fast_pow(int a, int p) {long long x = a, ans = 1ll;for( ; p; x = x * x % mod, p = p >> 1) if( p & 1 ) ans = x * ans % mod;return (int)ans;
}inline void Fast_numbertheory_transform(int *a, int limit, int type) {int rado = bit[limit];for(int i = 0; i < limit; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (rado - 1));for(int i = 0; i < limit; ++i) if( i < r[i] ) swap(a[i], a[r[i]]);for(int mid = 1; mid < limit; mid = mid << 1) {int Base_p = Fast_pow(3ll, (mod - 1) / (mid << 1));if( type == -1 ) Base_p = Fast_pow(Base_p, mod - 2);for(int l = 0, length = mid << 1; l < limit; l = l + length) {for(int k = 0, p = 1; k < mid; ++k, p = 1ll * p * Base_p % mod) {int x = a[l + k], y = 1ll * p * a[l + mid + k] % mod;a[l + k] = (x + y) % mod, a[l + mid + k] = (x - y + mod) % mod;}}}if( type == -1 ) for(int i = 0; i < limit; ++i) a[i] = 1ll * a[i] * Fast_pow(limit, mod - 2) % mod;
}int main(int argc, char const *argv[])
{freopen("..\\nanjolno.in", "r", stdin);freopen("..\\nanjolno.out", "w", stdout);scanf("%d%d", &n, &m);for(int i = 0; i <= n; ++i) f_1[i] = read();for(int i = 0; i <= m; ++i) f_2[i] = read();len = n + m, limit = 1, rado = 0;while( limit <= len ) limit = limit << 1, ++rado;for(int i = 0; i < limit; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (rado - 1));Fast_numbertheory_transform(f_1, 1);Fast_numbertheory_transform(f_2, 1);for(int i = 0; i < limit; ++i) f_1[i] = 1ll * f_1[i] * f_2[i] % mod;Fast_numbertheory_transform(f_1, -1);for(int i = 0; i <= len; ++i) printf("%d ", f_1[i]);fclose(stdin), fclose(stdout);return 0;
}
流螢斷續光,一明一滅一尺間,寂寞何以堪。
轉載于:https://www.cnblogs.com/nanjoqin/p/10357992.html
總結
以上是生活随笔為你收集整理的FFT FNT 简要整理的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。