QOJ.ac
QOJ
ID | Problem | Submitter | Result | Time | Memory | Language | File size | Submit time | Judge time |
---|---|---|---|---|---|---|---|---|---|
#848998 | #9945. Circular Convolution | Tobo | WA | 2ms | 4804kb | C++20 | 10.6kb | 2025-01-09 11:11:53 | 2025-01-09 11:12:02 |
Judging History
answer
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5, P = 998244353;
using i64 = long long;
using Poly = vector<int>;
/*---------------------------------------------------------------------------*/
#define MUL(a, b) (i64(a) * (b) % P)
#define ADD(a, b) (((a) += (b)) >= P ? (a) -= P : 0) // (a += b) %= P
#define SUB(a, b) (((a) -= (b)) < 0 ? (a) += P : 0) // ((a -= b) += P) %= P
Poly getInv(int L)
{
Poly inv(L);
inv[1] = 1;
for (int i = 2; i < L; i++)
inv[i] = MUL((P - P / i), inv[P % i]);
return inv;
}
int POW(i64 a, int b = P - 2, i64 x = 1)
{
for (; b; b >>= 1, a = a * a % P)
if (b & 1)
x = x * a % P;
return x;
}
auto inv = getInv(N);
struct Complex
{
double x = 0.0;
double y = 0.0;
Complex() {}
Complex(double x, double y) : x(x), y(y) {}
Complex operator+(const Complex &rhs) const { return Complex(x + rhs.x, y + rhs.y); }
Complex operator-(const Complex &rhs) const { return Complex(x - rhs.x, y - rhs.y); }
Complex operator*(const Complex &rhs) const { return Complex(x * rhs.x - y * rhs.y, x * rhs.y + y * rhs.x); }
Complex conj() const { return Complex(x, -y); }
};
namespace NTT
{
const int g = 3;
Poly Omega(int L)
{
int wn = POW(g, P / L);
Poly w(L);
w[L >> 1] = 1;
for (int i = L / 2 + 1; i < L; i++)
w[i] = MUL(w[i - 1], wn);
for (int i = L / 2 - 1; i >= 1; i--)
w[i] = w[i << 1];
return w;
}
auto W = Omega(1 << 18); // Length
void DIF(int *a, int n)
{
for (int k = n >> 1; k; k >>= 1)
for (int i = 0, y; i < n; i += k << 1)
for (int j = 0; j < k; ++j)
y = a[i + j + k], a[i + j + k] = MUL(a[i + j] - y + P, W[k + j]), ADD(a[i + j], y);
}
void IDIT(int *a, int n)
{
for (int k = 1; k < n; k <<= 1)
for (int i = 0, x, y; i < n; i += k << 1)
for (int j = 0; j < k; ++j)
x = a[i + j], y = MUL(a[i + j + k], W[k + j]),
a[i + j + k] = x - y < 0 ? x - y + P : x - y, ADD(a[i + j], y);
int Inv = P - (P - 1) / n;
for (int i = 0; i < n; i++)
a[i] = MUL(a[i], Inv);
reverse(a + 1, a + n);
}
}
/*---------------------------------------------------------------------------*/
namespace Polynomial
{
// basic operator
int norm(int n) { return 1 << (__lg(n - 1) + 1); }
void norm(Poly &a)
{
if (!a.empty())
a.resize(norm(a.size()), 0);
else
a = {0};
}
void DFT(Poly &a) { NTT::DIF(a.data(), a.size()); }
void IDFT(Poly &a) { NTT::IDIT(a.data(), a.size()); }
Poly &dot(Poly &a, Poly &b)
{
for (int i = 0; i < a.size(); i++)
a[i] = MUL(a[i], b[i]);
return a;
}
// mul / div int
Poly &operator*=(Poly &a, int b)
{
for (auto &x : a)
x = MUL(x, b);
return a;
}
Poly operator*(Poly a, int b) { return a *= b; }
Poly operator*(int a, Poly b) { return b * a; }
Poly &operator/=(Poly &a, int b) { return a *= POW(b); }
Poly operator/(Poly a, int b) { return a /= b; }
// Poly add / sub
Poly &operator+=(Poly &a, Poly b)
{
a.resize(max(a.size(), b.size()));
for (int i = 0; i < b.size(); i++)
ADD(a[i], b[i]);
return a;
}
Poly operator+(Poly a, Poly b) { return a += b; }
Poly &operator-=(Poly &a, Poly b)
{
a.resize(max(a.size(), b.size()));
for (int i = 0; i < b.size(); i++)
SUB(a[i], b[i]);
return a;
}
Poly operator-(Poly a, Poly b) { return a -= b; }
// Poly mul
Poly operator*(Poly a, Poly b)
{
int n = a.size() + b.size() - 1, L = norm(n);
if (a.size() <= 8 || b.size() <= 8)
{
Poly c(n);
for (int i = 0; i < a.size(); i++)
for (int j = 0; j < b.size(); j++)
c[i + j] = (c[i + j] + (i64)a[i] * b[j]) % P;
return c;
}
a.resize(L), b.resize(L);
DFT(a), DFT(b), dot(a, b), IDFT(a);
return a.resize(n), a;
}
// Poly inv
Poly Inv2k(Poly a)
{ // |a| = 2 ^ k
int n = a.size(), m = n >> 1;
if (n == 1)
return {POW(a[0])};
Poly b = Inv2k(Poly(a.begin(), a.begin() + m)), c = b;
b.resize(n), DFT(a), DFT(b), dot(a, b), IDFT(a);
for (int i = 0; i < n; i++)
a[i] = i < m ? 0 : P - a[i];
DFT(a), dot(a, b), IDFT(a);
return move(c.begin(), c.end(), a.begin()), a;
}
Poly Inv(Poly a)
{
int n = a.size();
norm(a), a = Inv2k(a);
return a.resize(n), a;
}
// Poly calculus
Poly deriv(Poly a)
{
for (int i = 1; i < a.size(); i++)
a[i - 1] = MUL(i, a[i]);
return a.pop_back(), a;
}
Poly integ(Poly a)
{
a.push_back(0);
for (int i = 1; i < a.size(); i++)
a[i] = MUL(inv[i], a[i - 1]);
return a[0] = 0, a;
}
// Poly ln
Poly Ln(Poly a)
{
int n = a.size();
a = deriv(a) * Inv(a);
return a.resize(n - 1), integ(a);
}
// Poly exp
Poly Exp(Poly a)
{
int n = a.size(), k = norm(n);
Poly b = {1}, c, d;
a.resize(k);
for (int L = 2; L <= k; L <<= 1)
{
d = b, b.resize(L), c = Ln(b), c.resize(L);
for (int i = 0; i < L; i++)
c[i] = a[i] - c[i] + (a[i] < c[i] ? P : 0);
ADD(c[0], 1), DFT(b), DFT(c), dot(b, c), IDFT(b);
move(d.begin(), d.end(), b.begin());
}
return b.resize(n), b;
}
// Poly pow
Poly Pow(Poly a, int b)
{
// a0 > 1 : f^k(x) = (f(x) * inv(a0))^k * a0^k
// a0 = 0 : 右移至第一个系数不为0的项,设为x^d,则设g(x) = f(x) / x^d
// 转化为问题一,然后乘上(x^d)^k
int n = a.size(), d = 0, k, a0;
while (d < n && !a[d])
++d;
if ((i64)d * b >= n)
return Poly(n);
a.erase(a.begin(), a.begin() + d); // f(x) / x^d
a0 = a[0];
k = POW(a0), norm(a *= k); // f(x) * inv(a0)
a = Exp(Ln(a) * b) * POW(a0, b);
a.resize(n), d *= b;
for (int i = n - 1; i >= 0; i--)
a[i] = i >= d ? a[i - d] : 0; // 乘上(x^d)^k
return a;
}
Poly Pow(Poly a, string str)
{
i64 k = 0, t = 0;
for (int i = 0; i < str.size(); i++)
{
k = (k * 10 + str[i] - '0') % P;
if (t <= P)
t = (t * 10 + str[i] - '0');
}
if (k < P && t >= P)
k += P;
return Pow(a, k);
}
pair<Poly, Poly> div(Poly f, Poly g)
{ // F(x) = G(x) * Q(x) + R(x)
int n = f.size() - 1, m = g.size() - 1;
if (n < m)
return pair<Poly, Poly>{Poly{0}, f};
Poly finv = f, ginv = g;
reverse(finv.begin(), finv.end());
reverse(ginv.begin(), ginv.end());
ginv.resize(n - m + 1);
Poly q = finv * Inv(ginv);
q.resize(n - m + 1);
reverse(q.begin(), q.end());
Poly r(m), tmp = g * q;
for (int i = 0; i < m; i++)
r[i] = (f[i] - tmp[i] + P) % P;
return pair<Poly, Poly>{q, r};
}
Poly LinearRecurrence(i64 n, Poly tmp)
{
Poly f{1}, g{0, 1};
while (n)
{
if (n & 1)
f = div(f * g, tmp).second;
g = div(g * g, tmp).second;
n >>= 1;
}
return f;
}
constexpr double PI = 3.14159265358979323846;
int binary_upper_bound_in_bits(int w)
{
if (w <= 0)
return 1;
const int highbit = 31 - __builtin_clz(w);
return highbit + 1;
}
void fft(vector<Complex> &a, int L, int flag)
{
// flag == 1 : forward; flag == -1 : inverse.
// L should be a power of 2.
int k = binary_upper_bound_in_bits(L - 1);
vector<Complex> w(L);
vector<int> rev(L);
for (int i = 0; i < L; ++i)
{
w[i] = Complex(cos(2 * PI / L * i), flag * sin(2 * PI / L * i));
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (k - 1));
if (i < rev[i])
swap(a[i], a[rev[i]]);
}
for (int l = 2; l <= L; l <<= 1)
{
int m = l >> 1;
for (int i = 0; i < L; i += l)
{
for (int k = 0; k < m; ++k)
{
Complex t = w[L / l * k] * a[i + k + m];
a[i + k + m] = a[i + k] - t;
a[i + k] = a[i + k] + t;
}
}
}
if (flag == -1)
for (int i = 0; i < L; ++i)
a[i].x /= L;
}
Poly multiply_mod_any(Poly f, Poly g)
{
int L = norm((int)f.size() + (int)g.size() - 1);
vector<Complex> a(L), b(L), c(L), d(L);
for (int i = 0; i < f.size(); ++i)
{
a[i].x = int(f[i]) >> 15;
b[i].x = int(f[i]) & 32767;
}
for (int i = 0; i < g.size(); ++i)
{
c[i].x = int(g[i]) >> 15;
d[i].x = int(g[i]) & 32767;
}
fft(a, L, 1);
fft(b, L, 1);
fft(c, L, 1);
fft(d, L, 1);
for (int i = 0; i < L; ++i)
{
Complex ta = a[i], tb = b[i], tc = c[i], td = d[i];
a[i] = ta * tc;
b[i] = ta * td + tb * tc;
c[i] = tb * td;
}
fft(a, L, -1);
fft(b, L, -1);
fft(c, L, -1);
Poly z((int)f.size() + (int)g.size() - 1);
for (int i = 0; i < z.size(); ++i)
{
z[i] = (((i64)(a[i].x + .5) << 30) +
((i64)(b[i].x + .5) << 15) +
(i64)(c[i].x + .5));
}
return z;
}
}
using namespace Polynomial;
void solve()
{
int n;
cin >> n;
Poly a(n), b(n);
for (int &i : a)
cin >> i;
for (int &i : b)
cin >> i;
auto ans = multiply_mod_any(a, b);
for (int i = n; i < ans.size(); i++)
ans[i - n] += ans[i];
for (int i = 0; i < n; i++)
cout << ans[i] << ' ';
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
// cin >> t;
while (t--)
solve();
}
Details
Tip: Click on the bar to expand more detailed information
Test #1:
score: 100
Accepted
time: 2ms
memory: 4800kb
input:
3 1 1 4 5 1 4
output:
13 22 25
result:
ok 3 number(s): "13 22 25"
Test #2:
score: -100
Wrong Answer
time: 0ms
memory: 4804kb
input:
3 1 2 3 -1 2 0
output:
32773 32768 32769
result:
wrong answer 1st numbers differ - expected: '5', found: '32773'