题解:P4723 【模板】常系数齐次线性递推
更好的阅读体验
BM 部分内容参考 OI-wiki 有关部分,致谢。
Bostan-Mori 算法
求
[x^k] \frac{P(x)}{Q(x)} ,k 很大。
注明一下:
可以看到,我们将原问题变为了
参考实现在最后。
常系数齐次线性递推
求
a_n = \sum \limits_{i = 1} ^ k f_{i} a_{n-i} 。
有了 Bostan-Mori 算法,我们尝试构造
假设
那么
那么我们把
同理
容易发现,我们构造
有了
然后套用上述 Bostan-Mori 即可求解
我的卷积板子实现得非常古早,用数组实现的。为了防止 NTT 后原多项式中出现不可名状的数值,我直接在卷积后把两个原多项式都清空了。所以有的数组要备份很多遍,代码显得比较丑陋,凑合看罢。
#include<bits/stdc++.h>
#define endl '\n'
#define N 1000006
using namespace std;
constexpr int MOD=998244353,G=3,invG=332748118;
int r[N];
int qpow(int x,int y)
{
int ret=1;
for(;y;y>>=1,x=1ll*x*x%MOD)if(y&1)ret=1ll*ret*x%MOD;
return ret;
}
void NTT(int len,int *a,int opt)
{
for(int i=0;i<len;i++)
if(i<r[i])swap(a[i],a[r[i]]);
for(int i=1;i<len;i<<=1)
{
int tmp=i<<1,Wn=qpow(opt==1?G:invG,(MOD-1)/tmp);
for(int j=0;j<len;j+=tmp)
{
int w=1,x,y;
for(int k=0;k<i;k++,w=1ll*w*Wn%MOD)
{
x=a[j+k],y=1ll*w*a[i+j+k]%MOD;
a[j+k]=(x+y)%MOD,a[i+j+k]=(x+MOD-y)%MOD;
}
}
}
}
void mul(int n,int m,int *a,int *b,int *ans)
{
int len=1,lg=0;
while(len<n+m)len<<=1,lg++;
for(int i=0;i<len;i++)r[i]=(r[i>>1]>>1)|((i&1)<<lg-1);
NTT(len,a,1),NTT(len,b,1);
for(int i=0;i<len;i++)a[i]=1ll*a[i]*b[i]%MOD;
NTT(len,a,-1); int inv=qpow(len,MOD-2);
for(int i=0;i<=len;i++)b[i]=ans[i]=0;
for(int i=0;i<n+m;i++)ans[i]=1ll*a[i]*inv%MOD;
for(int i=0;i<=len;i++)a[i]=0;
}
int _q[N],f[N],g[N];
int bostan_mori(int n,int m,int *p,int *q,int k)
{
for(;k;k>>=1)
{
for(int i=0;i<m;i++)_q[i]=q[i];
for(int i=1;i<m;i+=2)_q[i]=(MOD-_q[i])%MOD;
mul(n,m,p,_q,f);
for(int i=0;i<m;i++)_q[i]=q[i];
for(int i=1;i<m;i+=2)_q[i]=(MOD-_q[i])%MOD;
mul(m,m,q,_q,g);
int new_n=0,new_m=0;
for(int i=k&1;i<n+m-1;i+=2)p[new_n++]=f[i];
for(int i=0;i<m+m-1;i+=2)q[new_m++]=g[i];
n=new_n,m=new_m;
}
if(!n||!m)return 0;
return 1ll*p[0]*qpow(q[0],MOD-2)%MOD;
}
int n,k,q[N],qq[N],a[N],res[N];
main()
{
scanf("%d%d",&n,&k),q[0]=qq[0]=MOD-1;
for(int i=1;i<=k;i++)scanf("%d",&q[i]),qq[i]=q[i]=(q[i]%MOD+MOD)%MOD;
for(int i=0;i<k;i++)scanf("%d",&a[i]),a[i]=(a[i]%MOD+MOD)%MOD;
mul(k+1,k,q,a,res);
for(int i=k;i<N;i++)res[i]=0;
printf("%d\n",bostan_mori(k,k+1,res,qq,n));
return 0;
}