NTT优缺点:
能取模
没有精度差
一般来说常数小
NTT常数小,通常比一大堆浮点运算的FFT要快(通常嘛)
讲一下任意模数NTT
众所周知,为了满足单位根的性质,
N
T
T
NTT
NTT需要质数模数,而且需要质数模数能写成
p
=
a
∗
2
k
+
1
p=a*2^k+1
p=a∗2k+1的形式
比较常用的有998244353,1004535809,469762049,这三个原根都是3
n n n次多项式在模 m m m下乘积,最终系数一定不会大于 n m 2 nm^2 nm2找三个模数分别做 N T T NTT NTT再使用 C R T CRT CRT合并一下就好了,大概范围是 1 0 26 10^{26} 1026
//P4245
#define N 400005
int len, len1, len2, mod, lim = 1, L = 0;
int rev[N], F[N], G[N], A[N][3], B[N][3], ans[N];
int pr[3] = {469762049, 998244353, 1004535809};
int inv(int a, int b) {return fpow(a, b - 2, b);}
void NTTA(int u, int lim, int type) {
int p = pr[u];
int inv3 = fpow(3, p - 2, p);
for (int i = 0 ; i < lim ; i++ )
if (i < rev[i]) swap(A[i][u], A[rev[i]][u]);
for (int i = 1 ; i < lim ; i <<= 1) {
int wn = fpow(type == 1 ? 3 : inv3, (p - 1) / (i << 1), p);
for (int j = 0 ; j < lim ; j += i << 1) {
LL w = 1;
for (int k = 0 ; k < i ; k ++, w = (w * wn) % p) {
int x = A[j + k][u], y = 1ll * w * A[j + k + i][u] % p;
A[j + k][u] = (1ll * x + y + p) % p;
A[j + k + i][u] = (1ll * x - y + p) % p;
}
}
}
if (type == -1) {
int inv = fpow(lim, p - 2, p);
for (int i = 0; i < lim ; i ++) A[i][u] = 1ll * A[i][u] * inv % p;
}
}
void NTTB(int u, int lim, int type) {
int p = pr[u];
int inv3 = fpow(3, p - 2, p);
for (int i = 0 ; i < lim ; i++ )
if (i < rev[i]) swap(B[i][u], B[rev[i]][u]);
for (int i = 1 ; i < lim ; i <<= 1) {
int wn = fpow(type == 1 ? 3 : inv3, (p - 1) / (i << 1), p);
for (int j = 0 ; j < lim ; j += i << 1) {
LL w = 1;
for (int k = 0 ; k < i ; k ++, w = (w * wn) % p) {
int x = B[j + k][u], y = 1ll * w * B[j + k + i][u] % p;
B[j + k][u] = (1ll * x + y + p) % p;
B[j + k + i][u] = (1ll * x - y + p) % p;
}
}
}
if (type == -1) {
int inv = fpow(lim, p - 2, p);
for (int i = 0; i < lim ; i ++) B[i][u] = 1ll * B[i][u] * inv % p;
}
}
void CRT(){
len = len1 + len2;
LL a, b, c, t, k, M = 1ll * pr[0] * pr[1];
LL inv1 = inv(pr[1], pr[0]), inv0 = inv(pr[0], pr[1]),inv3 = inv(M % pr[2], pr[2]);
for (int i = 0; i <= len ; i++ ) {
a = A[i][0], b = A[i][1], c = A[i][2];
t = (mul(a * pr[1] % M, inv1, M) + mul(b * pr[0] % M, inv0, M)) % M;
k = ((c - t % pr[2]) % pr[2] + pr[2]) % pr[2] * inv3 % pr[2];
ans[i] = ((k % mod) * (M % mod) % mod + t % mod) % mod;
}
}
int main() {
len1 = rdi(), len2 = rdi(), mod = rdi();
while (lim <= (len1 + len2)) lim <<= 1, L ++;
for (int i = 0 ; i < lim ; i++ )rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1));
for (int i = 0 ; i <= len1 ; i++ ) F[i] = rdi(), A[i][2] = A[i][1] = A[i][0] = F[i];
for (int i = 0 ; i <= len2 ; i++ ) G[i] = rdi(), B[i][2] = B[i][1] = B[i][0] = G[i];
for (int u = 0 ; u <= 2 ; u++ ) {
NTTA(u, lim, 1), NTTB(u, lim, 1);
for (int i = 0 ; i < lim ; i++ ) A[i][u] = 1ll * A[i][u] * B[i][u] % pr[u];
NTTA(u, lim, -1);
}
CRT();
for (int i = 0 ; i <= len ; i++ ) write_(ans[i]);
}