常系数齐次线性递推
求一个(k)阶齐次线性递推数列(a)的第(n)项。
给定这个数列的前(k)项(a_0,a_1,cdots,a_n),和递推系数(f_1,f_2,cdots,f_k)
表示
矩阵优化
矩阵快速幂不能通过,但是是重要的思想
设转移矩阵为(A),假设我们构造出序列(c),使得
两边同时乘上初始行向量(a),注意到矩阵乘法的分配律
因为我们最终只需要求行向量的第(0)项
注意到等式右边,(a)是初始序列,(A^i)是(i)次转移的矩阵,那么((acdot A^i))就是(i)次转移之后的向量,((acdot A^i)_0)就是(i)次转移之后的第(0)个,即原序列的第(i)个
那么
多项式科技
问题转化为求(c_i),满足(A^n=sumlimits_{i=0}^{k-1}c_iA^i),右边是一个关于(A)的多项式
因为(sumlimits_{i=0}^{k-1}c_iA^i)的次数较小,考虑它是(A^n mod)另一个关于矩阵的多项式(G(A))之后的余项
将(A^n)换一种形式
(G(A))满足(G(A)=0),即
将(A)视为一个数(x),问题再一次转化,成为求(G),然后快速幂对(G)取模,即可得到一个关于(A)的多项式(R(A)=sumlimits_{i=0}^{k-1}c_iA^i)
有结论
然后多项式取模即可
scan(n);scan(k);
init_poly(k+k-1);
for(int i=1;i<=k;++i){
scan(f[i]);f[i]=dec(f[i]%mod,0);
R[k-i]=mod-f[i];
}
R[k]=1;
INVG=R;INVG.rev();INVG=Basic::poly_inv(INVG,k+1);
for(int i=0;i<k;++i){
scan(a[i]);a[i]=dec(a[i]%mod,0);
}
B[1]=1;F[0]=1;
for(;n;n>>=1,B=(B*B)%R)if(n&1)F=(F*B)%R;
int ans=0;for(int i=0;i<k;++i)Add(ans,1ll*F[i]*a[i]%mod);
printf("%d",ans);
多项式多点求值
给定多项式(f(x)),以及(m)个数(a_i),表示(m)次询问(f(a_i))的值
多项式科技
同上面的思路,将(F(x))表示成(F(x)=Q(x)G(x)+R(x))的形式。构造一个(G(x_0)),使得(G(x_0)=0),那么就有(F(x_0)=R(x_0))
就再一次变成了多项式取模,最后答案即为(F)取模后的常数项(因为只剩这一项了)
(G(x))的构造也很好想:(G(x)=x-x_0)
但是每次都直接取模时间无法承受
线段树分治
将(F(x))分别对(prodlimits_{i=l}^{mid}(x-x_i))和(prodlimits_{i=mid+1}^{r}(x-x_i))取模,两边递归分治即可
显然这个就是线段树分治的形式
poly tr[N];
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)>>1)
void build(int id,int l,int r){
if(l==r){tr[id][0]=mod-a[l];tr[id][1]=1;return;}
build(lid,l,mid);build(rid,mid+1,r);
tr[id]=tr[lid]*tr[rid];
}
int ans[N];
void work(int id,int l,int r,poly f){
f=f%tr[id];if(l==r){ans[l]=f[0];return;}
work(lid,l,mid,f);
work(rid,mid+1,r,f);
}
int main(){
scan(n);++n;scan(m);F.Read(n);
init_poly(max(n<<1,m<<1)+5);
for(int i=1;i<=m;++i)scan(a[i]);
build(1,1,m);
work(1,1,m,F);
for(int i=1;i<=m;++i)putint(ans[i],'
');
}