洛谷P3803 快速数论变化(NTT)模板题
前置知识
欧拉函数 \(\varphi(n)\):小于或等于 \(n\) 的正整数中与 \(n\) 互质的数的个数。
费马小定理:
\(a^p \equiv a (\mod p)\)
推论:
\(a^{-1} \equiv 1 (\mod p)\)
欧拉定理
\(a^{\varphi(n)} \equiv 1(\mod n)\)
原根
满足同余式 \(a^n \equiv 1(\mod m)\) 的最小正整数 \(n\) 存在,这个 \(n\) 称作 \(a\) 模 \(m\) 的阶,记作 \(\delta_m(a)\)
若 \(\gcd(a, m) = 1\) 且 \(\delta_m(a) = \varphi(m)\),则称 \(a\) 为模 \(m\) 的原根。
| FFT | NTT |
|---|---|
| \(w_n^k = e^{\frac{-2k\pi}{n} i}\) | \(g_n^k = ( G^\frac{p-1}{n} )^k \mod p\) |
示例程序
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 998244353, G = 3, Gi = 332748118;
ll fpow(ll a, ll b) {
ll res = 1;
for (ll t = a % mod; b; b >>= 1, t = t * t % mod)
if (b & 1ll)
res = res * t % mod;
return res;
}
ll inv(ll a) {
return fpow(a, mod - 2);
}
void ntt(vector<ll>&a, int type) {
int n = a.size();
if (n == 1) return;
vector<ll> a0(n/2), a1(n/2);
for (int i = 0; i < n/2; i++) {
a0[i] = a[i * 2];
a1[i] = a[i * 2 + 1];
}
ntt(a0, type);
ntt(a1, type);
ll wn = fpow(type == 1 ? G : Gi, (mod - 1) / n);
ll w = 1;
for (int k = 0; k < n/2; k++) {
ll t = w * a1[k] % mod;
a[k] = (a0[k] + t) % mod;
a[k + n/2] = (a0[k] - t + mod) % mod;
w = w * wn % mod;
}
}
int n, m, limit = 1;
int main() {
scanf("%d%d", &n, &m);
while (limit < n + m + 1)
limit <<= 1;
vector<ll> a(limit), b(limit);
for (int i = 0; i <= n; i++) {
scanf("%lld", &a[i]);
a[i] %= mod;
}
for (int i = 0; i <= m; i++) {
scanf("%lld", &b[i]);
b[i] %= mod;
}
ntt(a, 1);
ntt(b, 1);
for (int i = 0; i < limit; i++)
a[i] = a[i] * b[i] % mod;
ntt(a, -1);
ll invLimit = inv(limit);
for (int i = 0; i <= n+m; i++)
printf("%lld ", a[i] * invLimit % mod);
return 0;
}
浙公网安备 33010602011771号