import std.conv, std.functional, std.range, std.stdio, std.string; import std.algorithm, std.array, std.bigint, std.bitmanip, std.complex, std.container, std.math, std.numeric, std.regex, std.typecons; import core.bitop; class EOFException : Throwable { this() { super("EOF"); } } string[] tokens; string readToken() { for (; tokens.empty; ) { if (stdin.eof) { throw new EOFException; } tokens = readln.split; } auto token = tokens.front; tokens.popFront; return token; } int readInt() { return readToken.to!int; } long readLong() { return readToken.to!long; } real readReal() { return readToken.to!real; } bool chmin(T)(ref T t, in T f) { if (t > f) { t = f; return true; } else { return false; } } bool chmax(T)(ref T t, in T f) { if (t < f) { t = f; return true; } else { return false; } } int binarySearch(alias pred, T)(in T[] as) { int lo = -1, hi = cast(int)(as.length); for (; lo + 1 < hi; ) { const mid = (lo + hi) >> 1; (unaryFun!pred(as[mid]) ? hi : lo) = mid; } return hi; } int lowerBound(T)(in T[] as, T val) { return as.binarySearch!(a => (a >= val)); } int upperBound(T)(in T[] as, T val) { return as.binarySearch!(a => (a > val)); } // a^-1 (mod 2^64) long modInv(long a) in { assert(a & 1, "modInv: a must be odd"); } do { long b = ((a << 1) + a) ^ 2; b *= 2 - a * b; b *= 2 - a * b; b *= 2 - a * b; b *= 2 - a * b; return b; } // a^-1 (mod m) long modInv(long a, long m) in { assert(m > 0, "modInv: m > 0 must hold"); } do { long b = m, x = 1, y = 0, t; for (; ; ) { t = a / b; a -= t * b; if (a == 0) { assert(b == 1 || b == -1, "modInv: gcd(a, m) != 1"); if (b == -1) y = -y; return (y < 0) ? (y + m) : y; } x -= t * y; t = b / a; b -= t * a; if (b == 0) { assert(a == 1 || a == -1, "modInv: gcd(a, m) != 1"); if (a == -1) x = -x; return (x < 0) ? (x + m) : x; } y -= t * x; } } // 2^-31 a (mod M) long montgomery(long M)(long a) if (1 <= M && M <= 0x7fffffff && (M & 1)) in { assert(0 <= a && a < (M << 31), "montgomery: 0 <= a < 2^31 M must hold"); } do { enum negInvM = -modInv(M) & 0x7fffffff; const b = (a + ((a * negInvM) & 0x7fffffff) * M) >> 31; return (b >= M) ? (b - M) : b; } // FFT on Z / M Z with Montgomery multiplication (x -> 2^31 x) // G: primitive 2^K-th root of unity class FFT(long M, int K, long G) if (is(typeof(montgomery!(M)(0))) && K >= 0 && 0 < G && G < M) { import std.algorithm : swap; import core.bitop : bsf; int n, invN; long[] g; this(int n) in { assert(!(n & (n - 1)), "FFT.this: n must be a power of 2"); assert(4 <= n && n <= 1 << K, "FFT.this: 4 <= n <= 2^K must hold"); } do { this.n = n; this.invN = ((1L << 31) / n) % M; g.length = n + 1; g[0] = (1L << 31) % M; g[1] = (G << 31) % M; foreach (_; 0 .. K - bsf(n)) { g[1] = montgomery!(M)(g[1] * g[1]); } foreach (i; 2 .. n + 1) { g[i] = montgomery!(M)(g[i - 1] * g[1]); } assert(g[0] != g[n >> 1] && g[0] == g[n], "FFT.this: G must be a primitive 2^K-th root of unity"); for (int i = 0, j = 0; i < n >> 1; ++i) { if (i < j) { swap(g[i], g[j]); swap(g[n - i], g[n - j]); } for (int m = n >> 1; (m >>= 1) && !((j ^= m) & m); ) {} } } void fftMontgomery(long[] x, bool inv) in { assert(x.length == n, "FFT.fftMontgomery: |x| = n must hold"); } do { foreach_reverse (h; 0 .. bsf(n)) { const l = 1 << h; foreach (i; 0 .. n >> 1 >> h) { const gI = g[inv ? (n - i) : i]; foreach (j; i << 1 << h .. ((i << 1) + 1) << h) { const t = montgomery!(M)(gI * x[j + l]); if ((x[j + l] = x[j] - t) < 0) { x[j + l] += M; } if ((x[j] += t) >= M) { x[j] -= M; } } } } for (int i = 0, j = 0; i < n; ++i) { if (i < j) { swap(x[i], x[j]); } for (int m = n; (m >>= 1) && !((j ^= m) & m); ) {} } if (inv) { foreach (i; 0 .. n) { x[i] = montgomery!(M)(invN * x[i]); } } } long[] convolution(long[] a, long[] b) in { assert(a.length <= n, "FFT.convolution: |a| <= n must hold"); assert(b.length <= n, "FFT.convolution: |b| <= n must hold"); } do { auto x = new long[n], y = new long[n]; foreach (i; 0 .. a.length) { const t = a[i] % M; x[i] = (((t < 0) ? (t + M) : t) << 31) % M; } foreach (i; 0 .. b.length) { const t = b[i] % M; y[i] = (((t < 0) ? (t + M) : t) << 31) % M; } fftMontgomery(x, false); fftMontgomery(y, false); foreach (i; 0 .. n) { x[i] = montgomery!(M)(x[i] * y[i]); } fftMontgomery(x, true); foreach (i; 0 .. n) { x[i] = montgomery!(M)(x[i]); } return x; } } enum MO = 998244353; alias FFT0 = FFT!(MO, 23, 31L); void main() { try { for (; ; ) { const P = readInt; auto A = new long[P]; foreach (i; 1 .. P) { A[i] = readLong(); } auto B = new long[P]; foreach (i; 1 .. P) { B[i] = readLong(); } long[] divs; foreach (d; 1 .. P - 1) { if ((P - 1) % d == 0) { divs ~= d; } } long g; for (g = 2; ; ++g) { bool ok = true; foreach (d; divs) { long gg = g, gd = 1; for (long e = d; e; e >>= 1) { if (e & 1) gd = (gd * gg) % P; gg = (gg * gg) % P; } ok = ok && (gd != 1); } if (ok) { break; } } auto gs = new long[P - 1]; gs[0] = 1; foreach (i; 1 .. P - 1) { gs[i] = (gs[i - 1] * g) % P; } debug { writeln("gs = ", gs); } int fftN; for (fftN = 4; fftN < 2 * P; fftN <<= 1) {} auto fft = new FFT0(fftN); auto a = new long[2 * P]; auto b = new long[2 * P]; foreach (i; 0 .. P - 1) { a[i] = A[cast(int)(gs[i])]; b[i] = B[cast(int)(gs[i])]; } const c = fft.convolution(a, b); auto ans = new long[P]; foreach (i; 0 .. 2 * (P - 1)) { ans[cast(int)(gs[i % (P - 1)])] += c[i]; } foreach (i; 0 .. P) { ans[i] = (ans[i] % MO + MO) % MO; } foreach (i; 1 .. P) { if (i > 1) write(" "); write(ans[i]); } writeln(); } } catch (EOFException e) { } }