快速数论变换(NTT)+多项式全家桶
在系数均为整数的时候,可以用NTT代替FFT,这样不会出现精度问题。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long lld;
const int N = 20000005;
const lld g = 3, mod = 998244353;
lld r[N];
lld powe(lld a, lld b) {
lld base = 1;
while(b) {
if(b & 1) base = base * a % mod;
a = a * a % mod;
b >>= 1;
}
return base;
}
void init(int M)
{
int l = log2(M); r[0] = 0;
for(int i = 1; i < M; i++) {
r[i] = (r[i >> 1] >> 1 | (i & 1) << (l - 1));
}
}
void NTT(vector <lld> &a, int M, int type) {
init(M);
for(int i = 0; i < M; i++) {
if(i<r[i]) swap(a[i], a[r[i]]);
}
lld Wn, w;
for(int mid = 1; mid < M; mid <<= 1) {
Wn = powe(g, (mod - 1) / (mid << 1));
if(type == -1) Wn = powe(Wn, mod - 2);
int size = mid << 1;
for(int i = 0; i < M; i += size) {
int j = i + mid; w = 1;
for(int k = i; k < j; k++) {
lld x = a[k], y = w * a[k + mid] % mod;
a[k] = (x + y) % mod;
a[k + mid] = (x - y + mod) % mod;
w = w * Wn % mod;
}
}
}
if(type == -1) {
int iv = powe(M, mod - 2);
for (int i = 0; i < M; i++) {
a[i] = a[i] * iv % mod;
}
}
}
vector<lld> conv(vector<lld> a, vector<lld> b) {
int siz = a.size() + b.size();
int M = 1;
while(M < siz) M <<= 1;
a.resize(M); b.resize(M);
init(M); NTT(a, M, 1); NTT(b, M, 1);
for(int i = 0; i <= M; i++) {
a[i] = a[i] * b[i] % mod;
}
NTT(a, M, -1); a.resize(siz);
return a;
}
int n, m;
int main() {
// freopen("data.in", "r", stdin);
cin >> n >> m;
vector <lld> a(n + 2), b(m + 2);
for(int i = 0; i <= n; i++) {
cin >> a[i];
}
for(int i = 0; i <= m; i++) {
cin >> b[i];
}
vector <lld> c = conv(a, b);
for(int i = 0; i <= n + m; i++) {
cout << c[i] << " ";
}
return 0;
}
多项式乘法,求逆,开根
#include <bits/stdc++.h>
using namespace std;
typedef long long lld;
const int N = 400010;
const lld g = 3, mod = 998244353;
lld r[N];
lld powe(lld a, lld b) {
lld base = 1;
while(b) {
if(b & 1) base = base * a % mod;
a = a * a % mod;
b >>= 1;
}
return base;
}
lld inv2;
void init(int M) {
int l = log2(M); r[0] = 0;
for(int i = 1; i < M; i++) {
r[i] = (r[i >> 1] >> 1 | (i & 1) << (l - 1));
}
}
void NTT(vector <lld> &a, int M, int type)
{
init(M);
for(int i = 0; i < M; i++) {
if(i < r[i]) swap(a[i], a[r[i]]);
}
lld Wn, w;
for(int mid = 1; mid < M; mid <<= 1) {
Wn = powe(g, (mod - 1) / (mid << 1));
if(type == -1) Wn = powe(Wn, mod - 2);
int size = mid << 1;
for(int i = 0; i < M; i += size) {
int j = i + mid; w = 1;
for(int k = i; k < j; k++) {
lld x = a[k], y = w * a[k + mid] % mod;
a[k] = (x + y) % mod;
a[k + mid] = (x - y + mod) % mod;
w = w * Wn % mod;
}
}
}
if(type == -1) {
int iv = powe(M, mod - 2);
for (int i = 0; i < M; i++) {
a[i] = a[i] * iv % mod;
}
}
};
vector<lld> conv(vector<lld> a, vector<lld> b) { //return a * b
int siz = a.size() + b.size();
int M = 1;
while(M < siz) M <<= 1;
a.resize(M); b.resize(M);
NTT(a, M, 1); NTT(b, M, 1);
for(int i = 0; i <= M; i++) {
a[i] = a[i] * b[i] % mod;
}
NTT(a, M, -1); lld t = powe(M, mod - 2);
for(int i = 0; i <= M; i++) {
a[i] = a[i] * t % mod;
}
a.resize(siz); return a;
}
vector<lld> inv_t(N);
void polyinv(const vector<lld> &f, const int n, vector<lld> &h) { //h = f^{-1}
if(n == 1) {
h[0] = powe(f[0], mod - 2); return ;
}
polyinv(f, (n + 1) >> 1, h);
int len = 1; while(len < n * 2) len *= 2;
copy(f.begin(), f.begin() + n, inv_t.begin());
fill(inv_t.begin() + n, inv_t.begin() + len, 0);
// init(len);
NTT(inv_t, len, 1); NTT(h, len, 1);
for(int i = 0; i < len; i++) {
h[i] = (2ll - inv_t[i] * h[i] % mod + mod) % mod * h[i] % mod;
}
NTT(h, len, -1);
fill(h.begin() + n, h.begin() + len, 0);
}
vector<lld> tmp(N), tmp2(N);
void polysqrt(const vector<lld> &f, int n, vector<lld> &h) { // h = sqrt(f)
if(n == 1) {
h[0] = 1; return ;
}
polysqrt(f, (n + 1) >> 1, h);
int len = 1;
while(len < n * 2) {
len *= 2;
}
fill(tmp.begin(), tmp.begin() + len, 0); polyinv(h, n, tmp);
copy(f.begin(), f.begin() + n, tmp2.begin());
fill(tmp2.begin() + n, tmp2.begin() + len, 0);
NTT(tmp2, len, 1); NTT(tmp, len, 1); NTT(h, len, 1);
for(int i = 0; i < len; i++) {
h[i] = (1ll * inv2 * (h[i] + tmp[i] * tmp2[i] % mod) % mod) % mod;
}
NTT(h, len, -1);
fill(h.begin() + n, h.begin() + len, 0);
}
int n, m;
int main() {
// freopen("data.in", "r", stdin);
cin >> n;
vector<lld> g(N), ans(N);
g.clear(); ans.clear(); inv2 = powe(2, mod - 2);
for(int i = 0; i < n; i++) {
cin >> g[i];
}
polysqrt(g, n, ans);
for(int i = 0; i < n; i++) {
cout << ans[i] << " ";
}
return 0;
}
多项式对数,指数,快速幂
#include <bits/stdc++.h>
using namespace std;
typedef long long lld;
const int N = 400010;
const lld g = 3, mod = 998244353;
lld r[N];
lld powe(lld a, lld b) {
lld base = 1;
while(b) {
if(b & 1) base = base * a % mod;
a = a * a % mod;
b >>= 1;
}
return base;
}
lld inv2;
void init(int M) {
int l = log2(M); r[0] = 0;
for(int i = 1; i < M; i++) {
r[i] = (r[i >> 1] >> 1 | (i & 1) << (l - 1));
}
}
void NTT(vector <lld> &a, int M, int type) {
init(M);
for(int i = 0; i < M; i++) {
if(i < r[i]) swap(a[i], a[r[i]]);
}
lld Wn, w;
for(int mid = 1; mid < M; mid <<= 1) {
Wn = powe(g, (mod - 1) / (mid << 1));
if(type == -1) Wn = powe(Wn, mod - 2);
int size = mid << 1;
for(int i = 0; i < M; i += size) {
int j = i + mid; w = 1;
for(int k = i; k < j; k++) {
lld x = a[k], y = w * a[k + mid] % mod;
a[k] = (x + y) % mod;
a[k + mid] = (x - y + mod) % mod;
w = w * Wn % mod;
}
}
}
if(type == -1) {
int iv = powe(M, mod - 2);
for (int i = 0; i < M; i++) {
a[i] = a[i] * iv % mod;
}
}
};
vector<lld> inv_t(N);
void polyinv(const vector<lld> &f, const int n, vector<lld> &h) { //h = f^{-1}
if(n == 1) {
h[0] = powe(f[0], mod - 2); return ;
}
polyinv(f, (n + 1) >> 1, h);
int len = 1; while(len < n * 2) len *= 2;
copy(f.begin(), f.begin() + n, inv_t.begin());
fill(inv_t.begin() + n, inv_t.begin() + len, 0);
NTT(inv_t, len, 1); NTT(h, len, 1);
for(int i = 0; i < len; i++) {
h[i] = (2ll - inv_t[i] * h[i] % mod + mod) % mod * h[i] % mod;
}
NTT(h, len, -1);
fill(h.begin() + n, h.begin() + len, 0);
}
vector<lld> inv_f(N), rf(N);
void polyln(const vector<lld> &f, const int n, vector<lld> &g) {
int limit = 1, l = 0;
while(limit < n * 2) limit <<= 1, l++;
for(int i = 0; i < limit; i++) {
inv_f[i] = rf[i] = 0;
}
polyinv(f, n, inv_f);
for(int i = 0; i <= n; i++) {
rf[i] = 1ll * f[i + 1] * (i + 1) % mod;
}
NTT(rf, limit, 1); NTT(inv_f, limit, 1);
for(int i = 0; i < limit; i++) {
g[i] = 1ll * rf[i] * inv_f[i] % mod;
}
NTT(g, limit, -1);
fill(g.begin() + n + 1, g.begin() + limit, 0);
for(int i = n; i >= 1; i--) {
g[i] = g[i - 1] * powe(i, mod - 2) % mod;
}
g[0] = 0;
}
vector<lld> ln_f(N), res(N);
void polyexp(const vector<lld> &a, const int len, vector<lld> &b) {
if(len == 1) {
b[0] = 1; return ;
}
polyexp(a, (len + 1) >> 1, b); polyln(b, len, ln_f);
int limit = 1, l = 0;
while(limit < (len * 2)) limit <<= 1, l++;
fill(res.begin(), res.begin() + limit, 0);
for(int i = 0; i <= len; i++) {
res[i] = (a[i] - ln_f[i] + mod) % mod;
}
res[0]++;
NTT(res, limit, 1); NTT(b, limit, 1);
for(int i = 0; i < limit; i++) {
b[i] = b[i] * res[i] % mod;
}
NTT(b, limit, -1);
}
void polypow(const vector<lld> &f, const int n, lld k, vector<lld> &h) {
vector<lld> tmp(N);
polyln(f, n, tmp);
for(int i = 0; i < n; i++) {
tmp[i] = tmp[i] * k % mod;
}
polyexp(tmp, n, h);
}
int n; lld k = 0;
string kk;
int main() {
// freopen("data.in", "r", stdin);
cin >> n >> kk;
for(int i = 0; i < kk.size(); i++) {
k = k * 10 + (kk[i] - '0');
if(k > mod) k = k % mod;
}
vector<lld> a(N);
for(int i = 0; i < n; i++) {
cin >> a[i];
}
vector<lld> ans(N);
polypow(a, n, k, ans);
for(int i = 0; i < n; i++) {
cout << ans[i] << " ";
}
return 0;
}