【luogu P3803】【模板】多项式乘法(NTT)
【模板】多项式乘法(NTT)
题目链接:luogu P3803
题目大意
给你两个多项式,要你求它们的卷积。
思路
这次我们写 NTT 的做法。
它的优点就是它可以取模,而且不会有精度问题,而且会比 FFT 稍微快一点?
然而它的确定就是它的模数比较死板,一般就 \(998244353,1004535809,469762049\) 这些。(它们的原根都是 \(3\))
其实就是考虑用原根代替复数。
因为它是跟单位根有着相同的性质。
然后通过证明,我们可以得到 \(\omega_n\equiv g^{\frac{p-1}{n}}\mod p\)。
然后你就把 FFT 中的 \(\omega_i\) 都换掉就是 NTT 了。
然后这里我 \(p\) 取的是 \(998244353\),原根是 \(3\)。
代码
#include<cstdio>
#include<algorithm>
#define ll long long
#define mo 998244353
#define G 3
using namespace std;
int n, m, an[4000001];
int limit, ln;
ll a[4000001], b[4000001], Gv;
ll ksm(ll x, ll y) {
ll re = 1;
while (y) {
if (y & 1) re = (re * x) % mo;
x = (x * x) % mo;
y >>= 1;
}
return re;
}
void NTT(ll *now, int op) {
for (int i = 0; i < limit; i++)
if (i < an[i]) swap(now[i], now[an[i]]);
for (int mid = 1; mid < limit; mid <<= 1) {
ll Wn = ksm(op == 1 ? G : Gv, (mo - 1) / (mid << 1));
//如果这里是负的那就是逆元的次方
for (int R = (mid << 1), j = 0; j < limit; j += R) {
ll w = 1;
for (int k = 0; k < mid; k++, w = (w * Wn) % mo) {
ll x = now[j + k], y = w * now[j + mid + k] % mo;
now[j + k] = (x + y) % mo;
now[j + mid + k] = (x - y + mo) % mo;
}
}
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i <= n; i++) {
scanf("%d", &a[i]);
a[i] = (a[i] % mo + mo) % mo;
}
for (int i = 0; i <= m; i++) {
scanf("%d", &b[i]);
b[i] = (b[i] % mo + mo) % mo;
}
limit = 1;
while (limit <= n + m) {
limit <<= 1; ln++;
}
for (int i = 0; i < limit; i++)
an[i] = (an[i >> 1] >> 1) | ((i & 1) << (ln - 1));
Gv = ksm(G, mo - 2);
NTT(a, 1);
NTT(b, 1);
for (int i = 0; i < limit; i++)
a[i] = (a[i] * b[i]) % mo;
NTT(a, -1);
ll liv = ksm(limit, mo - 2);//除就变成逆元乘
for (int i = 0; i <= n + m; i++)
printf("%lld ", a[i] * liv % mo);
return 0;
}