FFT
阶的定义
若 ,且 ,对于 最小的 ,我们称之为 模 的阶,记做 。
原根定义
若 ,则称 为模 的一个原根。
NTT
摘自Menci的博客。
考虑一个质数 (其中 为 的幂)。定义其原根 为使得 互不相同的数。
精度比FFT高
#include <cstdio>
typedef long long LL;
const int N = 2097152+5, P = 998244353, G = 3, Gi = 332748118; //G是P的原根,Gi是G的逆元
LL a[N], b[N], R[N], n, m, L;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
void swap(LL &x, LL &y) {
x ^= y, y ^= x, x ^= y;
}
LL fast_pow(LL a, int p) {
LL res = 1;
for (; p; p >>= 1, a = a * a % P)
if (p & 1) res = res * a % P;
return res;
}
void ntt(LL *A, int f) {
for (int i = 0; i < n; ++i) if (i < R[i]) swap(A[i], A[R[i]]);
for (int i = 1; i < n; i <<= 1) {
LL wn = fast_pow(f == 1 ? G : Gi, (P - 1) / (i << 1));
for (int j = 0, r = i << 1; j < n; j += r) {
LL w = 1;
for (int k = 0; k < i; ++k, w = w * wn % P) {
int x = A[j+k], y = w * A[i+j+k] % P;
A[j+k] = (x + y) % P, A[i+j+k] = (x - y + P) % P;
}
}
}
}
int main() {
n = read(), m = read();
for (int i = 0; i <= n; ++i) a[i] = read();
for (int i = 0; i <= m; ++i) b[i] = read();
m += n; for (n = 1; n <= m; n <<= 1) ++L;
for (int i = 0; i < n; ++i) R[i] = (R[i>>1] >> 1) | ((i & 1) << (L - 1));
ntt(a, 1); ntt(b, 1);
for (int i = 0; i < n; ++i) a[i] = (a[i] * b[i]) % P;
ntt(a, -1);
LL inv = fast_pow(n, P - 2);
for (int i = 0; i <= m; ++i) printf("%lld ", a[i] * inv % P);
return 0;
}
任意模数NTT
题意:两个多项式做乘法, 。
我们发现这样的最大值是 ,
那么可以选三个满足NTT性质且乘起来 的模数分别NTT,最后中国剩余定理合并。
设 为卷积的结果,有
先用 合并前两个,