目录
- 多项式求逆
- 多项式除法/取模
- 多项式牛顿迭代法
- 多项式开根
- 多项式 ln
- 多项式 exp
- 多项式幂函数
多项式求逆
可以暴力递推,时间复杂度 。
倍增可以将之优化到
,我们先从
开始倍增,假设现在倍增到了
,得到了模
的逆元
,现在我们要求
,显然这时候求出来的
是
的一部分,有下列关系式:
考虑
的结果一定是形如
,怎么把模数扩展到
呢,可以考虑将它平方,那么平方过的结果形如
,有如下关系式:
然后就可以用卷积算了,时间复杂度
。
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 1000005;
const int MOD = 998244353;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,len,cur,lg,a[MAXN],b[MAXN],A[MAXN],B[MAXN],Rev[MAXN];
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0;i<len;i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2;s<=len;s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0;i<len;i+=s)
{
int x=1;
for(int j=0;j<t;j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0;i<len;i++)
a[i]=a[i]*inv%MOD;
}
void work(int n)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i],A[i]%=MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
signed main()
{
n=read();
for(int i=0;i<n;i++) a[i]=read();
b[0]=qkpow(a[0],MOD-2);
cur=1;
while(cur<n)
{
cur<<=1;
work(cur);
}
for(int i=0;i<n;i++)
printf("%lld ",b[i]);
}
多项式除法
给定 次多项式 , 次多项式 ( ),求出 次的商 和 次的余数
由于这里有余数 难于处理,我们可以考虑消去它的影响。
可以用一些奇技淫巧,比如说翻转多项式,我们来推一波柿子(
表示反转上标):
然后我们把上式模一个
,然后上市就变成了:
然后求一个多项式逆元用
即可,时间复杂度
。
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 1000005;
const int MOD = 998244353;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,m,k,len,lg,Rev[MAXN],A[MAXN],B[MAXN];
int a[MAXN],b[MAXN],c[MAXN],ar[MAXN],br[MAXN];
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0;i<len;i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2;s<=len;s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0;i<len;i+=s)
{
int x=1;
for(int j=0;j<t;j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0;i<len;i++)
a[i]=a[i]*inv%MOD;
}
void work(int n,int *a,int *b)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i],A[i]%=MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
void inv(int *a)
{
int cur=1,b[MAXN]={};
b[0]=qkpow(a[0],MOD-2);
while(cur<k+1)
{
cur<<=1;
work(cur,a,b);
}
for(int i=0;i<=k;i++)
a[i]=b[i];
}
void mul(int *a,int *b,int *c)
{
len=1;while(len<=n+k) len<<=1;
for(int i=0;i<=len;i++) A[i]=B[i]=0;
for(int i=0;i<=n;i++) A[i]=a[i];
for(int i=0;i<=k;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<=len;i++) A[i]=A[i]*B[i]%MOD;
NTT(A,len,-1);
for(int i=0;i<=k;i++)
c[i]=A[i];
}
signed main()
{
n=read();m=read();k=n-m;
for(int i=0;i<=n;i++) a[i]=ar[n-i]=read();
for(int i=0;i<=m;i++) b[i]=br[m-i]=read();
inv(br);
mul(ar,br,c);
for(int i=0;i<=k/2;i++)
swap(c[i],c[k-i]);
for(int i=0;i<=k;i++)
printf("%lld ",(c[i]%MOD+MOD)%MOD);
puts("");
//
len=1;while(len<=2*n) len<<=1;
NTT(a,len,1);NTT(b,len,1);NTT(c,len,1);
for(int i=0;i<=len;i++) a[i]=a[i]-b[i]*c[i],a[i]%=MOD;
NTT(a,len,-1);
for(int i=0;i<m;i++)
printf("%lld ",(a[i]%MOD+MOD)%MOD);
}
多项式牛顿迭代法
0x01 导数的芝士
下面的介绍引用自百度百科。
导数(Derivative),也叫导函数值。又名微商,是微积分中的重要基础概念。当函数y=f(x)的自变量x在一点x0上产生一个增量Δx时,函数输出值的增量Δy与自变量增量Δx的比值在Δx趋于0时的极限a如果存在,a即为在x0处的导数,记作f’(x0)或df(x0)/dx。
对于一个函数 ,它的导函数
0x02 泰勒展开的芝士
泰勒公式是将一个在x=x0处具有n阶导数的函数f(x)利用关于(x-x0)的n次多项式来逼近函数的方法。
若函数f(x)在包含x0的某个闭区间[a,b]上具有n阶导数,且在开区间(a,b)上具有(n+1)阶导数,则对闭区间[a,b]上任意一点x,成立下式:
其中, 表示f(x)的n阶导数,等号后的多项式称为函数f(x)在x0处的泰勒展开式,剩余的Rn(x)是泰勒公式的余项,是(x-x0)n的高阶无穷小。
0x03 牛顿迭代法
这个方法用来解决下列问题,有一个关于多项式 的方程 ,给定 ,解这个方程。
假设我们已经求出了在模
意义下的解
,考虑扩展到模
意义下,把
在
处泰勒展开:
考虑到
是形如
的形式,平方本质就是卷积,所以平方后被模
的答案就是
,泰勒展开第二项之后的项都会被模掉,所以柿子就变得简洁了:
很多问题都能用这个方法,比如多项式求逆,多项式开根。
多项式开根
给定多项式
,求
,满足
,为了套牛顿迭代法,我们设
(这里可以把
当常数项,
的自变量是一个多项式),我们已经求出了在模
的意义的解
有下列柿子:
时间复杂度
,
的常数项必须是
, 要不然我也没办法了
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 1000005;
const int MOD = 998244353;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,len,lg,Rev[MAXN];
int a[MAXN],b[MAXN],c[MAXN],A[MAXN],B[MAXN];
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0;i<len;i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2;s<=len;s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0;i<len;i+=s)
{
int x=1;
for(int j=0;j<t;j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0;i<len;i++)
a[i]=a[i]*inv%MOD;
}
void work(int n,int *a,int *b)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i],A[i]%=MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
void inv(int n,int *a,int *b)
{
int cur=1;
for(int i=0;i<n;i++) b[i]=0;
b[0]=qkpow(a[0],MOD-2);
while(cur<n)
{
cur<<=1;
work(cur,a,b);
}
}
void mul(int n,int *a,int *b,int *c)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=A[i]*B[i]%MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++)
c[i]=A[i];
}
signed main()
{
n=read();
for(int i=0;i<n;i++) a[i]=read();
int cur=1,inv2=qkpow(2,MOD-2);b[0]=1;
while(cur<n)
{
cur<<=1;
inv(cur,b,c);
mul(cur,a,c,c);
for(int i=0;i<cur;i++)
b[i]=inv2*(b[i]+c[i])%MOD;
}
for(int i=0;i<n;i++)
printf("%lld ",b[i]);
}
updata 2019,12,22:
刚学了扩展版的开根,等于普通开根加上对一个整数在模意义下开根,可以考虑使用原根,如果能找到是 ,那么 就是 ( 是 的一个原根)。
处理类似于
的柿子解
可以用bsgs算法(其中
),我是看
大佬的博客学的,先对柿子等价变化,设
,则:
的取值范围是
,我们先算出右边所有可能的值,放在
里面,然后用所有可能的
去看有没有能对的上的值,不难发现
去
的时候最优,时间复杂度
,关键代码。
int bsgs(int z)
{
map<int,int> mp;
int m=sqrt(MOD)+1,t=z,tt=0;
for(int i=0;i<m;i++)
{
mp[t]=i;
t=t*3%MOD;
}
t=tt=qkpow(3,m);
for(int i=1;i<=m+1;i++)
{
if(mp.count(t))//查找t是否在mp中出现过
return i*m-mp[t];
t=t*tt%MOD;
}
return 0;
}
int solve(int x)
{
int p=bsgs(x);
int ret=qkpow(3,p/2);
return min(ret,MOD-ret);//好像在这道版题中要求取更小的开方值
}
多项式求ln
对于一个多项式
,求
,先推一波柿子:
其中
是积分符号,其中积分与求导是逆运算,下面来解释一下这个柿子需要的芝士:
- ,这就是链式法则
明确这个柿子之后就可以做了,关于 运算的话把对应位乘上 的逆元,就是求导的逆运算。
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 1000005;
const int MOD = 998244353;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,len,lg,Rev[MAXN];
int a[MAXN],b[MAXN],A[MAXN],B[MAXN];
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0;i<len;i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2;s<=len;s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0;i<len;i+=s)
{
int x=1;
for(int j=0;j<t;j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0;i<len;i++)
a[i]=a[i]*inv%MOD;
}
void work(int n,int *a,int *b)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i],A[i]%=MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
void inv(int n,int *a,int *b)
{
int cur=1;
for(int i=0;i<n;i++) b[i]=0;
b[0]=qkpow(a[0],MOD-2);
while(cur<n)
{
cur<<=1;
work(cur,a,b);
}
}
void mul(int n,int *a,int *b,int *c)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=A[i]*B[i]%MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++)
c[i]=A[i];
}
signed main()
{
n=read();
for(int i=0;i<n;i++) a[i]=read();
inv(n,a,b);
for(int i=0;i<n;i++)
a[i]=a[i+1]*(i+1)%MOD;
mul(n,a,b,a);
printf("0");
for(int i=0;i<n-1;i++)
printf(" %lld",a[i]*qkpow(i+1,MOD-2)%MOD);
}
多项式exp
对于一个多项式
,求:
然后上面的柿子可以套牛顿迭代,上式就变成了:
的常数项一定是
,
的常数项一定是
,不要问我为什么。
然后用一下上面的多项式ln,这个问题就很简单了。
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 1000005;
const int MOD = 998244353;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,len,lg,Rev[MAXN],A[MAXN],B[MAXN];
int a[MAXN],b[MAXN],c[MAXN];
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0;i<len;i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2;s<=len;s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0;i<len;i+=s)
{
int x=1;
for(int j=0;j<t;j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0;i<len;i++)
a[i]=a[i]*inv%MOD;
}
void work(int n,int *a,int *b)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i],A[i]%=MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
void inv(int n,int *a,int *b)
{
int cur=1;
for(int i=0;i<n;i++) b[i]=0;
b[0]=qkpow(a[0],MOD-2);
while(cur<n)
{
cur<<=1;
work(cur,a,b);
}
}
void mul(int n,int *a,int *b,int *c)
{
len=1;while(len<=2*n) len<<=1;
for(int i=0;i<len;i++) A[i]=B[i]=0;
for(int i=0;i<n;i++) A[i]=a[i];
for(int i=0;i<n;i++) B[i]=b[i];
NTT(A,len,1);NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=A[i]*B[i]%MOD;
NTT(A,len,-1);
for(int i=0;i<n;i++)
c[i]=A[i];
}
void ln(int n,int *A,int *c)
{
int a[n]={},b[n]={};
for(int i=0;i<n;i++)
a[i]=A[i];
inv(n,a,b);
for(int i=0;i<n;i++)
a[i]=a[i+1]*(i+1)%MOD;
mul(n,a,b,a);
c[0]=0;
for(int i=1;i<n;i++)
c[i]=a[i-1]*qkpow(i,MOD-2)%MOD;
}
signed main()
{
n=read();
for(int i=0;i<n;i++) a[i]=read();
int cur=1;b[0]=1;
while(cur<n)
{
cur<<=1;
ln(cur,b,c);
for(int i=0;i<cur;i++)
c[i]=a[i]-c[i];
c[0]++;
len=1;while(len<=2*cur) len<<=1;
NTT(b,len,1);NTT(c,len,1);
for(int i=0;i<len;i++) b[i]=b[i]*c[i]%MOD;
NTT(b,len,-1);
}
for(int i=0;i<n;i++)
printf("%lld ",b[i]);
}
多项式幂函数
给定一个多项式 ,求它的 次幂( 为正整数)。
很容易想到多项式直接快速幂,时间复杂度 。
还有更快的方法,先考虑做一些等价变换:
这样做的话时间复杂度是
的,前提要求是
的常数项为
。
如果
的常数项不为
呢,那我们就找到最低的有值的一位
,然后就可以这样算:
然后就可以用常数项为
的方法做了,我的常数有点大,不开
竟然爆零,开了就能过。
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 1000005;
const int MOD = 998244353;
int read()
{
int num=0,flag=1;
char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),num%=MOD,c=getchar();
return num*flag;
}
int n,k,len,lg,Rev[MAXN];
int a[MAXN],b[MAXN],c[MAXN],A[MAXN],B[MAXN];
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0; i<len; i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2; s<=len; s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0; i<len; i+=s)
{
int x=1;
for(int j=0; j<t; j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0; i<len; i++)
a[i]=a[i]*inv%MOD;
}
void work(int n,int *a,int *b)
{
len=1;
while(len<=2*n) len<<=1;
for(int i=0; i<len; i++) A[i]=B[i]=0;
for(int i=0; i<n; i++) A[i]=a[i];
for(int i=0; i<n; i++) B[i]=b[i];
NTT(A,len,1);
NTT(B,len,1);
for(int i=0; i<len; i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i],A[i]%=MOD;
NTT(A,len,-1);
for(int i=0; i<n; i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
void inv(int n,int *a,int *b)
{
int cur=1;
for(int i=0; i<n; i++) b[i]=0;
b[0]=qkpow(a[0],MOD-2);
while(cur<n)
{
cur<<=1;
work(cur,a,b);
}
}
void mul(int n,int *a,int *b,int *c)
{
len=1;
while(len<=2*n) len<<=1;
for(int i=0; i<len; i++) A[i]=B[i]=0;
for(int i=0; i<n; i++) A[i]=a[i];
for(int i=0; i<n; i++) B[i]=b[i];
NTT(A,len,1);
NTT(B,len,1);
for(int i=0; i<len; i++) A[i]=A[i]*B[i]%MOD;
NTT(A,len,-1);
for(int i=0; i<n; i++)
c[i]=A[i];
}
void ln(int n,int *A,int *c)
{
int a[n*10]= {},b[n*10]= {};
for(int i=0; i<n; i++)
a[i]=A[i];
inv(n,a,b);
for(int i=0; i<n; i++)
a[i]=a[i+1]*(i+1)%MOD;
mul(n,a,b,a);
c[0]=0;
for(int i=1; i<n; i++)
c[i]=a[i-1]*qkpow(i,MOD-2)%MOD;
}
void exp(int n,int *a,int *b)
{
int cur=1,c[n*10]= {};
b[0]=1;
while(cur<n)
{
cur<<=1;
ln(cur,b,c);
for(int i=0; i<cur; i++)
c[i]=a[i]-c[i];
c[0]++;
len=1;
while(len<=2*cur) len<<=1;
NTT(b,len,1);
NTT(c,len,1);
for(int i=0; i<len; i++) b[i]=b[i]*c[i]%MOD;
NTT(b,len,-1);
}
}
signed main()
{
n=read();
k=read();
for(int i=0; i<n; i++)
a[i]=read();
for(int i=0; i<n; i++)
if(a[i])
{
for(int j=i; j<n; j++)
b[j-i]=a[j]*qkpow(a[i],MOD-2)%MOD;
ln(n,b,c);
for(int j=0; j<n; j++)
{
c[j]=c[j]*k%MOD;
b[j]=0;
}
exp(n,c,b);
int t=qkpow(a[i],k);
for(int j=0; j<n; j++)
c[j]=0;
for(int j=0; j<n; j++)
if(j+k*i<n)
c[j+k*i]=b[j]*t%MOD;
for(int j=0; j<n; j++)
printf("%lld ",c[j]);
break;
}
}