【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;
}
posted @ 2021-08-24 09:31  あおいSakura  阅读(37)  评论(0编辑  收藏  举报