多项式全家桶
多项式乘法
FFT
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
const double PI = acos(-1.0);
const int MAXN = 1e7 + 5;
inline int read() {
char ch;
while((ch = getchar()) && (ch < '0' || ch > '9'));
return ch - '0';
}
int n, m, len, lim = 1;
int r[MAXN];
struct complex {
double x, y;
complex (double xx = 0, double yy = 0) {x = xx, y = yy;}
complex operator + (complex b) {return complex(x + b.x, y + b.y);};
complex operator - (complex b) {return complex(x - b.x, y - b.y);};
complex operator * (complex b) {return complex(x * b.x - y * b.y, x * b.y + y * b.x);};
}f[MAXN], g[MAXN];
void FFT(complex *A, int op) {
for(int i = 0; i < lim; i++)
if(i < r[i]) swap(A[i], A[r[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
complex wn(cos(PI / mid), op * sin(PI / mid));
for(int r = mid << 1, j = 0; j < lim; j += r) {
complex w(1, 0);
for(int k = 0; k < mid; k++, w = w * wn) {
complex x = A[j + k], y = w * A[j + k + mid];
A[j + k] = x + y, A[j + k + mid] = x - y;
}
}
}
}
int main() {
scanf("%d%d",&n, &m);
for(int i = 0; i <= n; i++) f[i].x = read();
for(int i = 0; i <= m; i++) g[i].x = read();
len = n + m;
int l = 0;
while(lim <= n + m) lim <<= 1, l++;
for(int i = 0; i < lim; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
FFT(f, 1);
FFT(g, 1);
for(int i = 0; i <= lim; i++) f[i] = f[i] * g[i];
FFT(f, -1);
for(int i = 0; i <= n + m; i++)
printf("%d ",(int)(f[i].x / lim + 0.5));
return 0;
}
NTT
#include<cstdio>
#include<iostream>
#define LL long long
using namespace std;
inline int read() {
int res = 0, f = 1; char ch;
while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '0');
(ch == '0') ? f = -1 : res = ch - '0';
while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0';
return res * f;
}
int prnum[21];
inline void print(LL x) {
if(x == 0) putchar('0');
else {
if(x < 0) putchar('-'), x = -x;
register int prcnt = 0;
while(x) {
prnum[++prcnt] = x % 10;
x /= 10;
}
for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0');
}
}
const int MAXN = 1e6 + 5;
const LL P = 998244353, G = 3, Gi = 332748118;
int n, m;
int lim = 1, len, rev[MAXN << 2];
LL f[MAXN << 2], g[MAXN << 2];
inline LL expow(LL x, LL y) {
LL res = 1;
while(y) {
if(y & 1) res = res * x % P;
x = x * x % P, y >>= 1;
}
return res;
}
inline void NTT(LL *A, int op) {
for(int i = 0; i < lim; i++)
if(i > rev[i]) swap(A[i], A[rev[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
LL wn = expow((op == 1 ? G : Gi), (P - 1) / (mid << 1));
for(int r = (mid << 1), j = 0; j < lim; j += r) {
LL w = 1;
for(int k = 0; k < mid; k++, w = w * wn % P) {
LL x = A[j + k], y = w * A[j + k + mid] % P;
A[j + k] = (x + y) % P, A[j + k + mid] = ((x - y) % P + P) % P;
}
}
}
}
int main() {
n = read(); m = read();
for(int i = 0; i <= n; i++) f[i] = read();
for(int i = 0; i <= m; i++) g[i] = read();
while(lim <= n + m) lim <<= 1, ++len;
for(int i = 0; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1));
NTT(f, 1); NTT(g, 1);
for(int i = 0; i < lim; i++) f[i] = f[i] * g[i] % P;
NTT(f, -1);
LL inv = expow(lim, P - 2);
for(int i = 0; i <= n + m; i++)
print(f[i] * inv % P), putchar(' ');
return 0;
}
多项式乘法逆
求 \(g\) 使得 \(F*G \equiv 1\ ( mod\ x^n )\)。
设 \(F*H \equiv 1\ (mod\ x^{\lceil \frac{n}{2} \rceil} )\)。
两边同乘 \(F\),得:
当递归到常数项时,\(H_0\) 为 \(F_0\) 在模 \(p\) 意义下的逆元。
#include<cstdio>
#include<iostream>
#define LL long long
using namespace std;
inline LL read() {
LL res = 0, f = 1; char ch;
while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-');
(ch == '-') ? f = -1 : res = ch - '0';
while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0';
return res * f;
}
int prnum[21];
inline void print(LL x) {
if(x == 0) putchar('0');
else {
register int prcnt = 0;
while(x) {
prnum[++prcnt] = x % 10;
x /= 10;
}
for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0');
}
}
const int MAXN = 4e5 + 5;
const LL MOD = 998244353, G = 3, Gi = 332748118;
int n, rev[MAXN];
LL f[MAXN], g[MAXN], c[MAXN];
inline LL expow(LL x, LL y) {
LL res = 1;
while(y) {
if(y & 1) res = res * x % MOD;
y >>= 1, x = x * x % MOD;
}
return res;
}
inline void NTT(LL *A, int lim, int op) {
for(int i = 0; i < lim; i++)
if(i > rev[i]) swap(A[i], A[rev[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
LL wn = expow((op == 1) ? G : Gi, (MOD - 1) / (mid << 1));
for(int r = (mid << 1), j = 0; j < lim; j += r) {
LL w = 1;
for(int k = 0; k < mid; k++, w = wn * w % MOD) {
LL x = A[j + k], y = w * A[j + k + mid] % MOD;
A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD;
}
}
}
}
void solve(int len0, LL *a, LL *b) {
b[0] = expow(a[0], MOD - 2);
for(int len = 1; len < (len0 << 1); len <<= 1) {
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 0; i < len; i++) c[i] = a[i];
for(int i = len; i < lim; i++) c[i] = 0;
NTT(c, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++)
b[i] = ((2 - c[i] * b[i]) % MOD * b[i] % MOD + MOD) % MOD;
NTT(b, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD;
for(int i = len; i < lim; i++) b[i] = 0;
}
}
int main() {
n = read();
for(int i = 0; i < n; i++) f[i] = read();
solve(n, f, g);
for(int i = 0; i < n; i++) print(g[i]), putchar(' ');
return 0;
}
多项式开根
求 \(G\) 使得 \(G^2 \equiv F\ (mod\ x^n)\)
设 \(H^2 \equiv F\ (mod\ x^{\lceil \frac{n}{2} \rceil})\)
当递归到常数项时,令 \(H_0\) 为 \(F_0\) 在模 \(p\) 意义下的二次剩余。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<ctime>
#define LL long long
using namespace std;
inline int read() {
int res = 0, f = 1; char ch;
while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-');
(ch == '-') ? f = -1 : res = ch - '0';
while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0';
return res * f;
}
int prnum[21];
inline void print(LL x) {
if(x == 0) putchar('0');
else {
register int prcnt = 0;
while(x) {
prnum[++prcnt] = x % 10;
x /= 10;
}
for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0');
}
}
const int MAXN = 4e5 + 5;
const LL MOD = 998244353, G = 3, Gi = 332748118, inv2 = 499122177;
int n, rev[MAXN];
LL pow_i, f[MAXN], g[MAXN], h[MAXN], q[MAXN], c[MAXN];
struct complex {
LL a, b;
};
inline complex operator * (const complex &x, const complex &y) { return (complex){(x.a * y.a % MOD + x.b * y.b % MOD * pow_i % MOD) % MOD, (x.a * y.b % MOD + x.b * y.a % MOD) % MOD}; }
inline int randmod() {
return 1ll * rand() * rand() % MOD;
}
inline LL expow(LL x, LL y) {
LL res = 1;
while(y) {
if(y & 1) res = res * x % MOD;
y >>= 1, x = x * x % MOD;
}
return res;
}
inline complex expow_i(complex x, int y) {
complex res = {1, 0};
while(y) {
if(y & 1) res = res * x;
y >>= 1, x = x * x;
}
return res;
}
inline LL cipolla(LL x) {
x %= MOD;
if(x == 0) return 0;
if(expow(x, (MOD - 1) / 2) == MOD - 1) return -1;
LL a, res;
while(1) {
a = randmod();
pow_i = ((a * a % MOD - x) % MOD + MOD) % MOD;
if(expow(pow_i, (MOD - 1) / 2) == MOD - 1) break;
}
res = expow_i((complex){a, 1}, (MOD + 1) / 2).a;
return min(res, MOD - res);
}
void NTT(LL *A, int lim, int op) {
for(int i = 0; i < lim; i++)
if(i > rev[i]) swap(A[i], A[rev[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
LL wn = expow((op == 1 ? G : Gi), (MOD - 1) / (mid << 1));
for(int r = (mid << 1), j = 0; j < lim; j += r) {
LL w = 1;
for(int k = 0; k < mid; k++, w = w * wn % MOD) {
LL x = A[j + k], y = w * A[j + k + mid];
A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD;
}
}
}
}
void getinv(int len, LL *a, LL *b) {
if(len == 1) return b[0] = expow(a[0], MOD - 2), void();
getinv((len + 1) >> 1, a, b);
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 0; i < len; i++) c[i] = a[i];
for(int i = len; i < lim; i++) c[i] = 0;
NTT(c, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++)
b[i] = ((2ll - c[i] * b[i] % MOD) * b[i] % MOD + MOD) % MOD;
NTT(b, lim, 0);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD;
for(int i = len; i < lim; i++) b[i] = 0;
}
void getsqrt(int len, LL *a, LL *b) {
if(len == 1) return b[0] = cipolla(a[0]), void();
getsqrt((len + 1) >> 1, a, b);
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 0; i < lim; i++) h[i] = 0;
getinv(len, b, h);
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 0; i < len; i++) q[i] = a[i];
for(int i = len; i < lim; i++) q[i] = 0;
NTT(q, lim, 1); NTT(h, lim, 1);
for(int i = 0; i < lim; i++) q[i] = q[i] * h[i] % MOD;
NTT(q, lim, 0);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i < len; i++) b[i] = (b[i] + q[i] * inv % MOD) % MOD * inv2 % MOD;
for(int i = len; i < lim; i++) b[i] = 0;
}
int main() {
srand((unsigned)time(0));
n = read();
for(int i = 0; i < n; i++) f[i] = read();
getsqrt(n, f, g);
for(int i = 0; i < n; i++) print(g[i]), putchar(' ');
return 0;
}
多项式除法
求 \(Q, R\),使得 \(F(x)\equiv G(x)*Q(x) + R(x)\ (mod\ x^n)\quad (F(x) 共 n 项,G(x) 共 m 项)\)
若消除 \(R(x)\) 的影响,则可以直接通过多项式乘法逆求出 \(G(x)\),再直接代入,即可求出 \(R(x)\)。
设 \(A_R(x)=x^{deg F}A(\frac{1}{x})\)。
因为 \(Q(x)\) 仅有 \(n-m\) 项,所以模 \(x^{n-m+1}\) 并不影响。
所以先求出 \(G_R(x)\) 的逆,再做多项式乘法求出 \(Q(x)\)。
然后,通过
求出 \(R(x)\)。
写的时候注意边界问题,及时清零。
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
inline int read() {
int res = 0, f = 1; char ch;
while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '0');
(ch == '0') ? f = -1 : res = ch - '0';
while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0';
return res * f;
}
int prnum[21];
inline void print(LL x) {
if(x == 0) putchar('0');
else {
if(x < 0) putchar('-'), x = -x;
register int prcnt = 0;
while(x) {
prnum[++prcnt] = x % 10;
x /= 10;
}
for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0');
}
}
const int MAXN = 4e5 + 5;
const LL MOD = 998244353, G = 3, Gi = 332748118;
int n, m, rev[MAXN];
LL f[MAXN], g[MAXN], g0[MAXN], gr[MAXN], q[MAXN], c[MAXN];
inline LL expow(LL x, LL y) {
LL res = 1;
while(y) {
if(y & 1) res = res * x % MOD;
y >>= 1, x = x * x % MOD;
}
return res;
}
inline void NTT(LL *A, int lim, int op) {
for(int i = 0; i < lim; i++)
if(i > rev[i]) swap(A[i], A[rev[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
LL wn = expow((op == 1 ? G : Gi), (MOD - 1) / (mid << 1));
for(int r = (mid << 1), j = 0; j < lim; j += r) {
LL w = 1;
for(int k = 0; k < mid; k++, w = w * wn % MOD) {
LL x = A[j + k], y = w * A[j + k + mid] % MOD;
A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD;
}
}
}
}
void getinv(int len, LL *a, LL *b) {
if(len == 1) return b[0] = expow(a[0], MOD - 2), void();
getinv((len + 1) >> 1, a, b);
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 0; i < len; i++) c[i] = a[i];
for(int i = len; i < lim; i++) c[i] = 0;
NTT(c, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++)
b[i] = ((2 - c[i] * b[i]) % MOD * b[i] % MOD + MOD) % MOD;
NTT(b, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD;
for(int i = len; i < lim; i++) b[i] = 0;
}
void getmul(int lena, int lenb, int lenx, LL *a, LL *b) {
int lim = 1, tot = 0;
while(lim <= (lena + lenb)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
NTT(a, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++) a[i] = a[i] * b[i] % MOD;
NTT(a, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i <= lenx; i++) a[i] = a[i] * inv % MOD;
for(int i = lenx + 1; i < lim; i++) a[i] = 0;
}
int main() {
n = read(); m = read();
for(int i = 0; i <= n; i++) f[i] = q[n - i] = read();
for(int i = 0; i <= m; i++) g[i] = read(), g0[m - i] = g[i];
for(int i = n - m + 1; i <= m; i++) g0[i] = 0;
getinv(n - m + 1, g0, gr);
getmul(n, n - m, n - m, q, gr);
reverse(q, q + n - m + 1);
for(int i = 0; i <= n - m; i++) print(q[i]), putchar(' '); putchar('\n');
getmul(m, n - m, m, g, q);
for(int i = 0; i < m; i++) print(((f[i] - g[i]) % MOD + MOD) % MOD), putchar(' '); putchar('\n');
return 0;
}
多项式牛顿迭代
前置芝士
1.乘法求导法则:
2.链式求导法则:
3.可以 \(O(n)\) 对一个多项式进行求导和积分。设 \(f(x)=\sum_i a_ix^i\),则
- \(f(x)\) 在 \(a\) 处的泰勒展开为:
描述
给定 \(g(x)\),已知 \(f(x)\) 满足:
求在模 \(x^n\) 意义下的 \(f(x)\)。
牛顿迭代
倍增。
当 \(n = 1\) 时,\([x^0]g(f(x))\) 可求。
设 \(f(x)\) 在模 \(x^{\lceil \frac{n}{2} \rceil}\) 意义下的解为 \(f_0(x)\),要求在模 \(x^n\) 意义下的解 \(f(x)\)。
将 \(g(f(x))\) 在 \(f_0(x)\) 处进行泰勒展开,得:
因为 \(f(x)-f_0(x)\) 的最低非零项次数为 \(\lceil \frac{n}{2} \rceil\),所以
所以
所以
应用
多项式求逆
给定 \(h(x)\),求 \(f(x)=h^{-1}(x)\)。
设\(g(f(x))=\frac{1}{f(x)}-h(x)\),所以
多项式开方,除法等也可用此方法推出,不过咕咕咕。
多项式对数函数
这个东西不需要牛顿迭代,只是 \(exp\) 的前置知识。
给定 \(f(x)\),求 \(\ln(f(x))\)。
根据定义,若 \(\ln(f(x))\) 存在,则必须满足:
对 \(\ln(f(x))\) 求导再积分,得:
#include<cstdio>
#include<iostream>
#define LL long long
using namespace std;
inline LL read() {
LL res = 0, f = 1; char ch;
while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-');
(ch == '-') ? f = -1 : res = ch - '0';
while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0';
return res * f;
}
int prnum[21];
inline void print(LL x) {
if(x == 0) putchar('0');
else {
register int prcnt = 0;
while(x) {
prnum[++prcnt] = x % 10;
x /= 10;
}
for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0');
}
}
const int MAXN = 4e5 + 5;
const LL MOD = 998244353, G = 3, Gi = 332748118;
int n, rev[MAXN];
LL f[MAXN], g[MAXN], c[MAXN];
inline LL expow(LL x, LL y) {
LL res = 1;
while(y) {
if(y & 1) res = res * x % MOD;
y >>= 1, x = x * x % MOD;
}
return res;
}
inline void NTT(LL *A, int lim, int op) {
for(int i = 0; i < lim; i++)
if(i > rev[i]) swap(A[i], A[rev[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
LL wn = expow((op == 1) ? G : Gi, (MOD - 1) / (mid << 1));
for(int r = (mid << 1), j = 0; j < lim; j += r) {
LL w = 1;
for(int k = 0; k < mid; k++, w = w * wn % MOD) {
LL x = A[j + k], y = w * A[j + k + mid] % MOD;
A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD;
}
}
}
}
void getinv(int len, LL *a, LL *b) {
if(len == 1) return b[0] = expow(a[0], MOD - 2), void();
getinv((len + 1) >> 1, a, b);
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 0; i < len; i++) c[i] = a[i];
for(int i = len; i < lim; i++) c[i] = 0;
NTT(c, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++)
b[i] = ((2 - c[i] * b[i]) % MOD * b[i] % MOD + MOD) % MOD;
NTT(b, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD;
for(int i = len; i < lim; i++) b[i] = 0;
}
void getln(int len, LL *a, LL *b) {
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 1; i < len; i++) c[i - 1] = a[i] * i % MOD; c[len - 1] = 0;
for(int i = len; i < lim; i++) c[i] = 0;
NTT(c, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++) b[i] = c[i] * b[i] % MOD;
NTT(b, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = len - 1; i >= 1; i--) b[i] = b[i - 1] * inv % MOD * expow(i, MOD - 2) % MOD; b[0] = 0;
for(int i = len; i < lim; i++) b[i] = 0;
}
int main() {
n = read();
for(int i = 0; i < n; i++) f[i] = read();
getinv(n, f, g);
getln(n, f, g);
for(int i = 0; i < n; i++) print(g[i]), putchar(' ');
return 0;
}
多项式指数函数
给定 \(h(x)\),求 \(f(x)=e^{h(x)}\)。
设 \(g(f(x))=\ln(f(x))-h(x)\),所以
#include<cstdio>
#include<iostream>
#define LL long long
using namespace std;
inline LL read() {
LL res = 0, f = 1; char ch;
while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-');
(ch == '-') ? f = -1 : res = ch - '0';
while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0';
return res * f;
}
int prnum[21];
inline void print(LL x) {
if(x == 0) putchar('0');
else {
register int prcnt = 0;
while(x) {
prnum[++prcnt] = x % 10;
x /= 10;
}
for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0');
}
}
const int MAXN = 4e5 + 5;
const LL MOD = 998244353, G = 3, Gi = 332748118;
int n, rev[MAXN];
LL f[MAXN], g[MAXN], h[MAXN], c[MAXN];
inline LL expow(LL x, LL y) {
LL res = 1;
while(y) {
if(y & 1) res = res * x % MOD;
y >>= 1, x = x * x % MOD;
}
return res;
}
inline void NTT(LL *A, int lim, int op) {
for(int i = 1; i < lim; i++)
if(i > rev[i]) swap(A[i], A[rev[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
LL wn = expow((op == 1) ? G : Gi, (MOD - 1) / (mid << 1));
for(int r = (mid << 1), j = 0; j < lim; j += r) {
LL w = 1;
for(int k = 0; k < mid; k++, w = w * wn % MOD) {
LL x = A[j + k], y = w * A[j + k + mid] % MOD;
A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD;
}
}
}
}
void getinv(int len, LL *a, LL *b) {
if(len == 1) return b[0] = expow(a[0], MOD - 2), void();
getinv((len + 1) >> 1, a, b);
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 0; i < len; i++) c[i] = a[i];
for(int i = len; i < lim; i++) c[i] = 0;
NTT(c, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++)
b[i] = ((2 - b[i] * c[i]) % MOD * b[i] % MOD + MOD) % MOD;
NTT(b, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD;
for(int i = len; i < lim; i++) b[i] = 0;
}
void getln(int len, LL *a, LL *b) {
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(int i = 1; i < len; i++) c[i - 1] = a[i] * i % MOD; c[len - 1] = 0;
for(int i = len; i < lim; i++) c[i] = 0;
NTT(c, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++) b[i] = c[i] * b[i] % MOD;
NTT(b, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = len - 1; i >= 1; i--) b[i] = b[i - 1] * inv % MOD * expow(i, MOD - 2) % MOD; b[0] = 0;
for(int i = len; i < lim; i++) b[i] = 0;
}
void getexp(int len, LL *a, LL *b) {
if(len == 1) return b[0] = 1, void();
getexp((len + 1) >> 1, a, b);
int lim = 1, tot = 0;
while(lim < (len << 1)) lim <<= 1, ++tot;
for(int i = 0; i < lim; i++) h[i] = 0;
getinv(len, b, h); getln(len, b, h);
for(int i = 1; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1));
h[0] = ((1 - h[0] + a[0]) % MOD + MOD) % MOD;
for(int i = 1; i < len; i++)
h[i] = ((a[i] - h[i]) % MOD + MOD) % MOD;
NTT(h, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++) b[i] = b[i] * h[i] % MOD;
NTT(b, lim, -1);
LL inv = expow(lim, MOD - 2);
for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD;
for(int i = len; i < lim; i++) b[i] = 0;
}
int main() {
n = read();
for(int i = 0; i < n; i++) f[i] = read();
getexp(n, f, g);
for(int i = 0; i < n; i++) print(g[i]), putchar(' ');
return 0;
}