问题引入

给出数列【学习小记】常系数齐次线性递推_矩阵乘法,满足当【学习小记】常系数齐次线性递推_多项式_02
【学习小记】常系数齐次线性递推_矩阵乘法_03
【学习小记】常系数齐次线性递推_矩阵乘法_04时,【学习小记】常系数齐次线性递推_多项式_05

m比较小,n特别大,快速计算【学习小记】常系数齐次线性递推_矩阵乘法_06

Newbie的解法

暴力递推计算

时间复杂度【学习小记】常系数齐次线性递推_特征值_07

Pupil的解法

可以将转移和数列都写成【学习小记】常系数齐次线性递推_多项式_08的矩阵的形式,矩阵快速幂即可

时间复杂度【学习小记】常系数齐次线性递推_特征值_09

Master的解法

我们需要一些数学知识进行铺垫:

Part 1 矩阵的特征值与特征多项式

我们知道一个矩阵乘一个列向量仍然是一个列向量。

若对于m阶矩阵A,有常数【学习小记】常系数齐次线性递推_特征值_10,非零列向量【学习小记】常系数齐次线性递推_多项式_11,满足【学习小记】常系数齐次线性递推_矩阵乘法_12则称【学习小记】常系数齐次线性递推_特征值_10为矩阵A的特征值,【学习小记】常系数齐次线性递推_多项式_11为矩阵的特征向量

上式也可以写作【学习小记】常系数齐次线性递推_特征值_15其中【学习小记】常系数齐次线性递推_矩阵乘法_16为单位矩阵
此式有解的充要条件是【学习小记】常系数齐次线性递推_矩阵乘法_17,即矩阵【学习小记】常系数齐次线性递推_特征值_18的行列式为0

【学习小记】常系数齐次线性递推_矩阵乘法_19可以看做是关于【学习小记】常系数齐次线性递推_特征值_10的一个m次多项式,记作【学习小记】常系数齐次线性递推_多项式_21【学习小记】常系数齐次线性递推_多项式_21称作矩阵A的特征多项式,对于矩阵A的任意一个特征值【学习小记】常系数齐次线性递推_多项式_23,都有【学习小记】常系数齐次线性递推_特征值_24

Part 2 Hamilton-Cayley theorem

对于矩阵,也一样的定义多项式运算(把多项式中的x换乘矩阵A),加法就是直接对应相加,常数乘法就按位相乘,乘法是矩阵乘法,0次方是单位矩阵,它的结果仍然是一个矩阵。

显然,矩阵多项式满足交换律,即【学习小记】常系数齐次线性递推_矩阵乘法_25成立。
简单证明:考虑某两项相乘的结果【学习小记】常系数齐次线性递推_多项式_26,由于前后都是A,矩阵乘法满足结合律,因此指数可以任意分配,换成【学习小记】常系数齐次线性递推_特征值_27也是可以的

哈密顿—凯莱定理:对于矩阵A的特征多项式【学习小记】常系数齐次线性递推_矩阵乘法_28,满足【学习小记】常系数齐次线性递推_多项式_29

证明网上到处都有,此处就不赘述了。

Part 3 求解转移矩阵的特征多项式

回到原题,我们对于Pupil解法的转移矩阵A,求解它的特征多项式
考虑矩阵【学习小记】常系数齐次线性递推_特征值_18

它长这样:
【学习小记】常系数齐次线性递推_多项式_31

根据行列式的定义,将第一行展开
【学习小记】常系数齐次线性递推_矩阵乘法_32
其中【学习小记】常系数齐次线性递推_矩阵乘法_33表示矩阵A的代数余子式,即挖掉第i行和第j列以后剩下的矩阵的行列式。

我们发现所有的余子矩阵都是下三角矩阵,行列式就是对角线乘积。

化简整理,可得【学习小记】常系数齐次线性递推_矩阵乘法_34
负号都被行列式里面逆序对个数的负号消掉了。

Part 4 计算答案

我们设要求的数列【学习小记】常系数齐次线性递推_矩阵乘法的初始矩阵为【学习小记】常系数齐次线性递推_矩阵乘法_36,它是一个m行1列的矩阵(列向量),从第m行到第1行分别为【学习小记】常系数齐次线性递推_特征值_37(注意顺序是反的)
实际上我们想知道的【学习小记】常系数齐次线性递推_矩阵乘法_06就是矩阵【学习小记】常系数齐次线性递推_矩阵乘法_39的第m行第一列的值。

此时的关键就是【学习小记】常系数齐次线性递推_多项式_40,因为【学习小记】常系数齐次线性递推_多项式_41非常大,无法直接计算

然而根据前面的铺垫,我们有【学习小记】常系数齐次线性递推_多项式_29【学习小记】常系数齐次线性递推_多项式_40我们可以看做只有一项的一个关于A的多项式

那么根据多项式除法相关知识,可以得到【学习小记】常系数齐次线性递推_多项式_44,其中【学习小记】常系数齐次线性递推_矩阵乘法_45的次数是小于【学习小记】常系数齐次线性递推_特征值_46的次数也就是小于m的,【学习小记】常系数齐次线性递推_矩阵乘法_45相当于多项式【学习小记】常系数齐次线性递推_多项式_40对多项式【学习小记】常系数齐次线性递推_特征值_46取模

可能会有这样的疑问,【学习小记】常系数齐次线性递推_多项式_29怎么能作除数呢?
其实并不要紧,我们并不需要知道【学习小记】常系数齐次线性递推_特征值_46的实际值,我们相当于将【学习小记】常系数齐次线性递推_多项式_40减去了若干个【学习小记】常系数齐次线性递推_特征值_46,将次数降低了,而结果不变。

实现上来说,由于【学习小记】常系数齐次线性递推_特征值_54的系数已知,我们可以先将式子里的矩阵A换成变量【学习小记】常系数齐次线性递推_多项式_55,代入,利用多项式取模算出Q的系数,然后再将x换回A,这样得出来的Q的系数是相同的。并且计算【学习小记】常系数齐次线性递推_矩阵乘法_56【学习小记】常系数齐次线性递推_多项式_57的结果是一样的。

为了求出【学习小记】常系数齐次线性递推_矩阵乘法_58的系数,我们可以采用快速幂的做法,初始【学习小记】常系数齐次线性递推_多项式_59,然后不断的自己与自己相乘,乘完对多项式【学习小记】常系数齐次线性递推_矩阵乘法_28取模
这一部分如果暴力取模,时间复杂度为【学习小记】常系数齐次线性递推_多项式_61
如果采用NTT优化多项式取模,时间复杂度为【学习小记】常系数齐次线性递推_矩阵乘法_62
这样求出了【学习小记】常系数齐次线性递推_矩阵乘法_45的系数,不妨设【学习小记】常系数齐次线性递推_多项式_64
要求矩阵【学习小记】常系数齐次线性递推_矩阵乘法_56的第m行第一列的值

也就是【学习小记】常系数齐次线性递推_矩阵乘法_66的第m行第一列
然而【学习小记】常系数齐次线性递推_特征值_67的第m行第一列的值就是【学习小记】常系数齐次线性递推_矩阵乘法_68

所以【学习小记】常系数齐次线性递推_矩阵乘法_69

还有一种情况,前m项并没有直接给出,也是通过递推得出的,暴力递推求前m项的复杂度是【学习小记】常系数齐次线性递推_矩阵乘法_70
考虑优化

考虑数列【学习小记】常系数齐次线性递推_矩阵乘法的一般生成函数【学习小记】常系数齐次线性递推_矩阵乘法_72(与矩阵G不同)
转移序列【学习小记】常系数齐次线性递推_特征值_73的一般生成函数【学习小记】常系数齐次线性递推_特征值_74

由于【学习小记】常系数齐次线性递推_矩阵乘法_72是无限长的一个序列,我们可以得到【学习小记】常系数齐次线性递推_多项式_76
其中【学习小记】常系数齐次线性递推_多项式_77是一个常数,相当于第0项

移项,可以得到【学习小记】常系数齐次线性递推_多项式_78
在模【学习小记】常系数齐次线性递推_矩阵乘法_79意义下多项式求逆即可
时间复杂度是【学习小记】常系数齐次线性递推_矩阵乘法_80

模板题([BZOJ4161] Shlw loves matrixI)

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 4005
#define mo 1000000007
#define LL long long
using namespace std;
LL f[N],g[N],h[N],s1[N],a[N],u1[N];
int n,m;
void mul(LL *x,LL *y,LL *z)
{
fo(i,0,2*m-2) u1[i]=0;
fo(i,0,m-1) fo(j,0,m-1) u1[i+j]=(u1[i+j]+x[i]*y[j])%mo;
fod(i,2*m-2,m)
{
fo(j,0,m) u1[i-m+j]=(u1[i-m+j]-f[j]*u1[i])%mo;
}
fo(i,0,m-1) z[i]=u1[i];
}
int main()
{
cin>>n>>m;
fo(i,1,m) scanf("%lld",&a[i]),f[m-i]=-a[i];
f[m]=1;
g[1]=1;
s1[0]=1;
for(int t=n;t;t>>=1)
{
if(t&1) mul(s1,g,s1);
mul(g,g,g);
}
fo(i,0,m-1) scanf("%lld",&h[i]);
LL ans=0;
fo(i,0,m-1) ans=(ans+s1[i]*h[i]%mo+mo)%mo;
printf("%lld\n",ans);
}