多项式进阶操作
看到成爷爷写了一篇就跟风写一篇吧,慢慢更
一些前置技能
泰勒展开
ddy:这里用大家小学二年级学的泰勒展开。。。
对于一个不是很好表示的函数\(f(x)\),我们可以用一个多项式函数来拟合它,用神奇的泰勒展开即可
这里的\(n\)越大,误差就越小;由于常规的多项式操作都是在\({\rm mod}\ x^n\)意义下,所以我们只需要展开前\(n\)项即可
多项式牛顿迭代
定义一个多项式\(F(x)\),求一个多项式\(G(x)\)满足\(F(G(x))\equiv 0({\rm mod}\ x^n)\)
考虑现在有\(F(A(x))\equiv 0({\rm mod}\ x^n)\),推出\(F(B(x))\equiv 0({\rm mod}\ x^{2n})\)
不难发现有\(B-A\equiv 0({\rm mod}\ x^n)\),则\((B-A)^2\equiv 0({\rm mod}\ x^n)\)
我们在\(A\)处对\(F(B)\)做泰勒展开
由于\((B-A)^2\equiv 0({\rm mod}\ x^n)\),所以后面的那些东西相当于没有,只剩下了
整理一下即
注意一下\(F(B)\equiv 0({\rm mod}\ x^{2n})\),于是
1.多项式求逆
就是给定多项式\(F(x)\),求一个多项式\(G(x)\)
使得
\(mod\ x^n\)就是只考虑前\(n\)项
这是一个基于倍增的算法,就是推一下如何从\(mod\ x^{\frac{n}{2}}\)推到\(mod\ x^n\)
设多项式\(B'(x)\)满足
求多项式\(B(x)\)满足
后面那个忽略前\(n\)项,所以前\(\frac{n}{2}\)项肯定会满足
和第一个柿子一减
显然\(F\)不可能是\(0\),于是只能是\((B-B')=0\)
于是
两边平方并且乘上\(F\)
别忘了\(F(x)B(x)\equiv 1(mod\ x^{\frac{n}{2}})\)就有
发现这个东西可以倍增来求,就是需要一个\(NTT\)了
时间复杂度\(T(n)=T(n/2)+O(nlogn)=O(nlogn)\)
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 300005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const LL mod=998244353;
const LL g[2]={332748118,3};
int n,rev[maxn],len;
LL a[maxn],b[maxn],C[maxn];
inline LL quick(LL a,LL b) {LL S=1;while(b) {if(b&1ll) S=S*a%mod;b>>=1ll;a=a*a%mod;}return S;}
inline void NTT(LL *f,int o) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1;LL og1=quick(g[o],(mod-1)/i);
for(re int l=0;l<len;l+=i) {
LL og=1,t;
for(re int x=l;x<l+ln;x++) {
t=(og*f[ln+x])%mod;
f[ln+x]=(f[x]-t+mod)%mod;
f[x]=(f[x]+t)%mod;
og=(og*og1)%mod;
}
}
}
if(o) return;
LL inv=quick(len,mod-2);
for(re int i=0;i<len;i++) f[i]=(f[i]*inv)%mod;
}
inline void Inv(int n,LL *A,LL *B) {
if(n==1) {B[0]=quick(A[0],mod-2);return;}
Inv((n+1)>>1,A,B);len=1;
while(len<=n+n-2) len<<=1;
for(re int i=0;i<n;i++) C[i]=A[i];
for(re int i=n;i<len;i++) C[i]=0;
for(re int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?len>>1:0);
NTT(C,1),NTT(B,1);
for(re int i=0;i<len;i++) B[i]=(2ll*B[i]%mod-C[i]*B[i]%mod*B[i]+mod)%mod,B[i]=(B[i]+mod)%mod;
NTT(B,0);for(re int i=n;i<len;i++) B[i]=0;
}
int main()
{
n=read();
for(re int i=0;i<n;i++) a[i]=read();
Inv(n,a,b);
for(re int i=0;i<n;i++) printf("%lld ",b[i]);
return 0;
}
2.分治\(NTT\)
拿luogu上的板子吧
给定\(G=\{g_1,g_2,g_3...\}\)
求
原先没学生成函数,自然做不动了,现在用生成函数来求就很休闲了
设\(f\)的生成函数为\(F(x)\)
就有
发现里面是\(F(x)\times G(x)\)
于是就有
解得
这是生成函数的关系,从多项式来看,就是对\(1-G(x)\)求一个逆
于是还是套多项式求逆的板子
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
const int maxn=262144+10;
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const LL G[2]={3,332748118};
const LL mod=998244353;
LL g[maxn],b[maxn],C[maxn];
int rev[maxn];
int n,len;
inline LL quick(LL a,LL b) {LL S=1;while(b){if(b&1) S=S*a%mod;b>>=1;a=a*a%mod;}return S;}
inline void NTT(LL *f,int v) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1;LL og1=quick(G[v],(mod-1)/i);
for(re int l=0;l<len;l+=i) {
LL t,og=1;
for(re int x=l;x<l+ln;x++) {
t=(og*f[ln+x])%mod;
f[x+ln]=(f[x]-t+mod)%mod;
f[x]=(f[x]+t)%mod;
og*=og1;og%=mod;
}
}
}
if(!v) return;
LL inv=quick(len,mod-2);
for(re int i=0;i<len;i++) f[i]=(f[i]*inv)%mod;
}
inline void Inv(int n,LL *A,LL *B) {
if(n==1) {B[0]=quick(A[0],mod-2);return;}
Inv((n+1)>>1,A,B);len=1;
while(len<=n+n-2) len<<=1;
for(re int i=0;i<n;i++) C[i]=A[i];
for(re int i=n;i<len;i++) C[i]=0;
for(re int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?len>>1:0);
NTT(C,0),NTT(B,0);
for(re int i=0;i<len;i++) B[i]=(2ll*B[i]%mod-C[i]*B[i]%mod*B[i]+mod)%mod,B[i]=(B[i]+mod)%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
int main() {
n=read();g[0]=1;
for(re int i=1;i<n;i++) g[i]=mod-read();
Inv(n,g,b);
for(re int i=0;i<n;i++) printf("%d ",(int)b[i]);
return 0;
}
3.多项式开根
就是给你一个多项式\(F(x)\),让你找到一个\(B(x)\),满足\(B^2(x)\equiv F(x) \ [n]\)
和求逆一样,也是考虑倍增假设我们现在求得了\(B'^2(x)\equiv F\ [\frac{n}{2}]\),考虑推出\(B^2(x)\equiv F\ [n]\)
前\(n\)项相同肯定也满足前\(\frac{n}{2}\)项相同
于是
和\(B'^2(x)\equiv F(x)\ [\frac{n}{2}]\)做一个差
左右两边加上\(4B^2(x)B'^2(x)\)
发现开根还需要套上一个求逆,但是复杂度依旧是\(O(nlogn)\)
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=262144+1005;
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const LL mod=998244353;
const LL G[2]={3,332748118};
int rev[maxn],len,n;
LL A[maxn],B[maxn],C[maxn],D[maxn],K[maxn],H[maxn];
inline LL ksm(LL a,LL b) {LL S=1;while(b) {if(b&1) S=S*a%mod;b>>=1;a=a*a%mod;}return S;}
inline void NTT(LL *f,int o) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1;LL og1=ksm(G[o],(mod-1)/i);
for(re int l=0;l<len;l+=i) {
LL t,og=1;
for(re int x=l;x<l+ln;x++) {
t=(f[ln+x]*og)%mod;
f[x+ln]=(f[x]-t+mod)%mod;
f[x]=(f[x]+t)%mod;
og=(og*og1)%mod;
}
}
}
if(!o) return;
LL inv=ksm(len,mod-2);
for(re int i=0;i<len;i++) f[i]=(f[i]*inv)%mod;
}
inline void Inv(int n,LL *A,LL *B) {
if(n==1) {B[0]=ksm(A[0],mod-2);return;}
Inv((n+1)>>1,A,B);len=1;
while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
for(re int i=0;i<n;i++) C[i]=A[i];
for(re int i=n;i<len;i++) C[i]=0;
NTT(C,0),NTT(B,0);
for(re int i=0;i<len;i++) B[i]=(2ll*B[i]-C[i]*B[i]%mod*B[i]%mod+mod)%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
inline void mul(LL *A,LL *B,int n) {
len=1;while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
NTT(A,0),NTT(B,0);
for(re int i=0;i<len;i++) A[i]=(A[i]*B[i])%mod;
NTT(A,1);for(re int i=n;i<len;i++) A[i]=0;
}
inline void Sqrt(int n,LL *A,LL *B) {
if(n==1) {B[0]=1;return;}
Sqrt((n+1)>>1,A,B);
memset(K,0,sizeof(K));
memset(D,0,sizeof(D));
memset(H,0,sizeof(H));
for(re int i=0;i<n;i++) K[i]=(B[i]*2ll)%mod;Inv(n,K,D);
for(re int i=0;i<n;i++) H[i]=B[i];mul(B,H,n);
for(re int i=0;i<n;i++) B[i]=(B[i]+A[i])%mod;mul(B,D,n);
}
int main() {
n=read();
for(re int i=0;i<n;i++) A[i]=read();
Sqrt(n,A,B);
for(re int i=0;i<n;i++) printf("%lld ",B[i]);
return 0;
}
4.多项式求导和积分
不会,于是就背过吧
如果有
那么我们对\(F\)求导就有
显然,求导和积分是逆操作,于是我们对\(F\)求积分就有
5.多项式对数函数
我们求一个多少项式\(G(x)\),满足\(G(x)=ln\ F(x)\)
我们先两边求导
对于\(x\)来说满足\(ln'x=\frac{1}{x}\),于是
我们这样求出来是\(G'(x)\),之后求个积分就好了
于是
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=262144+5;
const int mod=998244353;
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int G[2]={3,332748118};
int n,len,rev[maxn],F[maxn],K[maxn],T[maxn],g[maxn],H[maxn],inv[maxn];
inline int ksm(int a,int b) {
int S=1;
while(b) {if(b&1) S=(1ll*S*a)%mod;b>>=1;a=(1ll*a*a)%mod;}
return S;
}
inline void NTT(int *f,int o) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1,og1=ksm(G[o],(mod-1)/i);
for(re int l=0;l<len;l+=i) {
int t,og=1;
for(re int x=l;x<l+ln;++x) {
t=(1ll*f[x+ln]*og)%mod;
f[x+ln]=(f[x]-t+mod)%mod;
f[x]=(f[x]+t)%mod;
og=(1ll*og*og1)%mod;
}
}
}
if(!o) return;
int Inv=ksm(len,mod-2);
for(re int i=0;i<len;i++) f[i]=(1ll*f[i]*Inv)%mod;
}
void Inv(int n,int *A,int *B) {
if(n==1) {B[0]=ksm(A[0],mod-2);return;}
Inv((n+1)>>1,A,B);
len=1;while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
for(re int i=0;i<n;i++) K[i]=B[i],T[i]=A[i];
for(re int i=n;i<len;i++) K[i]=T[i]=0;
NTT(K,0),NTT(T,0);
for(re int i=0;i<len;i++)
B[i]=(2ll*K[i]-1ll*T[i]*K[i]%mod*K[i]%mod+mod)%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
int main() {
n=read();
for(re int i=0;i<n;i++) F[i]=read();
for(re int i=1;i<n;i++) g[i-1]=(1ll*i*F[i])%mod;
Inv(n,F,H);len=1;while(len<n+n) len<<=1;
NTT(H,0),NTT(g,0);
for(re int i=0;i<len;i++) H[i]=(1ll*H[i]*g[i])%mod;
NTT(H,1);putchar('0');putchar(' ');
inv[1]=1;
for(re int i=2;i<n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(re int i=1;i<n;i++) printf("%d ",1ll*H[i-1]*inv[i]%mod);
return 0;
}
6.多项式指数函数
首先前置的东西是牛顿迭代
是这样的,如果我们有一个多项式\(F(x)\),需要求一个多项式\(G(x)\)满足
假设我们已经求出了\(F(G_0(x))\equiv 0\ [\frac{n}{2}]\)
那么
现在我们要求的东西是\(G(x)=e^{F(x)}\)
两边取一个\(\ln\)
设\(A(G)=\ln G-F\),把\(F\)看成一个常数项,显然有\(A'(G)=\frac{1}{G}\)
假设我们已经求出了
需要求
直接套上牛顿迭代
于是我们倍增就好啦
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=262144+5;
const int mod=998244353;
const int G[2]={3,332748118};
int n,inv[maxn],a[maxn],b[maxn],K[maxn];
int H[maxn],T[maxn],g[maxn],C[maxn],rev[maxn],len;
inline int ksm(int a,int b) {
int S=1;
while(b) {if(b&1) S=(1ll*S*a)%mod;b>>=1;a=(1ll*a*a)%mod;}
return S;
}
inline void NTT(int *f,int o) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1,og1=ksm(G[o],(mod-1)/i);
for(re int l=0;l<len;l+=i) {
int t,og=1;
for(re int x=l;x<l+ln;++x) {
t=(1ll*f[ln+x]*og)%mod;
f[x+ln]=(f[x]-t+mod)%mod;
f[x]=(f[x]+t)%mod;
og=(1ll*og*og1)%mod;
}
}
}
if(!o) return;
int Inv=inv[len];
for(re int i=0;i<len;i++) f[i]=(1ll*f[i]*Inv)%mod;
}
void Inv(int n,int *A,int *B) {
if(n==1) {B[0]=ksm(A[0],mod-2);return;}
Inv((n+1)>>1,A,B);
len=1;while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
for(re int i=0;i<n;i++) K[i]=A[i];
for(re int i=n;i<len;i++) K[i]=0;
NTT(K,0),NTT(B,0);
for(re int i=0;i<len;i++)
B[i]=(2ll*B[i]-1ll*K[i]*B[i]%mod*B[i]%mod+mod)%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
void Ln(int n,int *A,int *B) {
for(re int i=0;i<n;i++) g[i]=0,B[i]=0;
for(re int i=1;i<n;i++) g[i-1]=(1ll*i*A[i])%mod;
memset(C,0,sizeof(C));Inv(n,A,C);
len=1;while(len<n+n) len<<=1;
NTT(C,0),NTT(g,0);
for(re int i=0;i<len;i++) g[i]=(1ll*g[i]*C[i])%mod;
NTT(g,1);
for(re int i=1;i<n;i++) B[i]=(1ll*inv[i]*g[i-1])%mod;
}
void Exp(int n,int *A,int *B) {
if(n==1) {B[0]=1;return;}
Exp((n+1)>>1,A,B);
Ln(n,B,T);len=1;while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
for(re int i=0;i<n;i++) T[i]=(A[i]-T[i]+mod)%mod;
for(re int i=n;i<len;i++) T[i]=0;T[0]++;
NTT(B,0),NTT(T,0);
for(re int i=0;i<len;i++) B[i]=(1ll*B[i]*T[i])%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
int main() {
n=read();
for(re int i=0;i<n;i++) a[i]=read();
inv[1]=1;
for(re int i=2;i<=262144;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
Exp(n,a,b);
for(re int i=0;i<n;i++) printf("%d ",b[i]);
return 0;
}
7.多项式快速幂
显然直接裸上快速幂是两个\(log\)的
我们考虑要求的东西是\(A^k(x)\)
直接取一个\(\ln\)就能把\(k\)从指数上拿下来啦,之后我们得到的是\(\ln A^k\),再\(exp\)回去就好了
于是就是
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=262144+5;
const int mod=998244353;
const int G[2]={3,332748118};
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int n,len,rev[maxn],inv[maxn],T[maxn],C[maxn];
int a[maxn],K[maxn],g[maxn],H[maxn],O[maxn],m;
inline int getPow() {
int x=0;char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(10ll*x+c-48)%mod,c=getchar();
return x;
}
inline int ksm(int a,int b) {
int S=1;
while(b) {if(b&1) S=(1ll*S*a)%mod;b>>=1;a=(1ll*a*a)%mod;}
return S;
}
inline void NTT(int *f,int o) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1,og1=ksm(G[o],(mod-1)/i);
for(re int l=0;l<len;l+=i) {
int t,og=1;
for(re int x=l;x<l+ln;++x) {
t=(1ll*f[ln+x]*og)%mod;
f[ln+x]=(f[x]-t+mod)%mod;
f[x]=(f[x]+t)%mod;
og=(1ll*og*og1)%mod;
}
}
}
if(!o) return;
int Inv=inv[len];
for(re int i=0;i<len;i++) f[i]=(1ll*f[i]*Inv)%mod;
}
void Inv(int n,int *A,int *B) {
if(n==1) {B[0]=ksm(A[0],mod-2);return;}
Inv((n+1)>>1,A,B);
for(re int i=0;i<n;i++) T[i]=A[i];
for(re int i=n;i<len;i++) T[i]=0;
len=1;while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
NTT(T,0),NTT(B,0);
for(re int i=0;i<len;i++)
B[i]=(2ll*B[i]-1ll*T[i]*B[i]%mod*B[i]%mod+mod)%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
void Ln(int n,int *A,int *B) {
len=1;while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) g[i]=B[i]=0;
for(re int i=1;i<n;i++) g[i-1]=(1ll*i*A[i])%mod;
memset(C,0,sizeof(C));Inv(n,A,C);
len=1;while(len<n+n) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
NTT(C,0),NTT(g,0);
for(re int i=0;i<len;i++) C[i]=(1ll*g[i]*C[i])%mod;
NTT(C,1);for(re int i=1;i<n;i++) B[i]=(1ll*inv[i]*C[i-1])%mod;
}
void Exp(int n,int *A,int *B) {
if(n==1) {B[0]=1;return;}
Exp((n+1)>>1,A,B);Ln(n,B,K);
len=1;while(len<n+n) len<<=1;
for(re int i=0;i<n;i++) K[i]=(A[i]-K[i]+mod)%mod;
for(re int i=n;i<len;i++) K[i]=0;K[0]++;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
NTT(B,0),NTT(K,0);
for(re int i=0;i<len;i++) B[i]=(1ll*B[i]*K[i])%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
int main() {
inv[1]=1;
for(re int i=2;i<=262144;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
n=read(),m=getPow();
for(re int i=0;i<n;i++) a[i]=read();
Ln(n,a,H);
for(re int i=0;i<n;i++) H[i]=(1ll*H[i]*m)%mod;
Exp(n,H,O);
for(re int i=0;i<n;i++) printf("%d ",O[i]);
return 0;
}
8.多项式带余除法
就是给定两个多项式\(F(x),G(x)\),求满足如下条件的两个多项式
其中\(F(x)\)为\(n\)次多项式,\(G(x)\)为\(m\)次多项式,要求\(Q(x)\)为\(n-m\)次多项式,\(R(x)\)最高次项小于\(m\)
有一个非常牛逼的东西,定义\(F_r(x)\)为多项式\(F(x)\)的转置
那么就有
这非常显然
于是我们来化柿子了
于是多项式求逆即可
求出\(G_r(x)\)显然\(R(x)\)就很好求了
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int mod=998244353;
const int G[2]={3,(mod+1)/3};
const int maxn=262144+5;
int n,m,len;
int C[maxn],a[maxn],b[maxn],res[maxn],rev[maxn],ar[maxn],br[maxn],T[maxn],K[maxn];
inline int ksm(int a,int b) {
int S=1;for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) S=1ll*S*a%mod;return S;
}
inline void NTT(int *f,int o) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1,og1=ksm(G[o],(mod-1)/i);
for(re int t,og=1,l=0;l<len;l+=i,og=1)
for(re int x=l;x<l+ln;++x) {
t=1ll*og*f[x+ln]%mod,og=1ll*og*og1%mod;
f[x+ln]=(f[x]-t+mod)%mod,f[x]=(f[x]+t)%mod;
}
}
if(!o) return;
int Inv=ksm(len,mod-2);
for(re int i=0;i<len;i++) f[i]=1ll*f[i]*Inv%mod;
}
inline void Rev(int n,int *A,int *B) {
for(re int i=0;i<n;i++) A[i]=B[n-i-1];
}
inline void Inv(int n,int *A,int *B) {
if(n==1) {B[0]=ksm(A[0],mod-2);return;}
Inv((n+1)>>1,A,B);
len=1;while(len<n+n-1) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
for(re int i=0;i<n;i++) C[i]=A[i];
for(re int i=n;i<len;i++) C[i]=0;
NTT(C,0),NTT(B,0);
for(re int i=0;i<len;i++) B[i]=(2ll*B[i]-1ll*C[i]*B[i]%mod*B[i]%mod+mod)%mod;
NTT(B,1);for(re int i=n;i<len;i++) B[i]=0;
}
int main() {
n=read();m=read();
for(re int i=0;i<=n;i++) a[i]=read();
for(re int i=0;i<=m;i++) b[i]=read();
Rev(m+1,br,b),Rev(n+1,ar,a);
Inv(n-m+1,br,T);
len=1;while(len<n+1+n-m+1) len<<=1;
for(re int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
NTT(T,0),NTT(ar,0);
for(re int i=0;i<len;i++) ar[i]=1ll*ar[i]*T[i]%mod;
NTT(ar,1);for(re int i=n-m;i>=0;i--) printf("%d ",ar[i]);
for(re int i=n-m+1;i<len;i++) ar[i]=0;
Rev(n-m+1,K,ar);puts("");
NTT(K,0),NTT(b,0);
for(re int i=0;i<len;i++) b[i]=1ll*b[i]*K[i]%mod;
NTT(b,1);
for(re int i=0;i<m;i++) printf("%d ",(a[i]-b[i]+mod)%mod);
return 0;
}