版权声明:既然是蒟蒻写的文,那么各位大爷就将就着看吧~ https://blog.csdn.net/alan_cty/article/details/83065815
前言
上了高二学了数列,知道了如何给出递推求通项,也从数竞同学那里听来了高阶常系数齐次线性递推的通项求法。
那么OI上如何应用呢?
百度了一下发现自己在这一块的技能点为0,就决定学一学QwQ
线性代数渣没办法
特征多项式
若有常数
λ,向量
v
,对于n阶矩阵A满足
λv
=Av
那么称
λ为A的特征值,
v
为A的特征向量
有一个定理,秩为k的矩阵有k组线性无关的特征向量
我们把式子变一下形
(A−λI)v
=0
I是单位矩阵
这个式子有解的充要条件为
det(A−λI)=0
左边的东西显然是一个关于
λ的n次多项式,称作A的特征多项式
这个多项式的n个根就是n组特征值了
Cayley-Hamilton定理
设矩阵A的特征多项式为f(x),那么f(A)=0
这里f(A)可以理解成把多项式里面的乘法换成矩阵乘法
证明:考虑写成
f(A)=∏i=1n(λiI−A)
如果对于每个特征向量
vi
都有
vi
f(A)=0,由于这些特征向量线性无关,所以有f(A)=0
考虑一个特征向量和其对应的特征值
vi
(λiI−A)=vi
λiI−vi
A
根据定义这个东西为0
Q.E.D
矩阵乘法不满足交换律?其实在这里满足,把式子拆一下就好了
求解常系数齐次线性递推转移矩阵的特征多项式
特征多项式可以直接差值求,但在我们的这个问题里可以不用
⎝⎜⎜⎜⎜⎜⎜⎛a1100...0a2010...0a3001...0..................an000...1⎠⎟⎟⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎜⎜⎛hn−1hn−2hn−3hn−4...h0⎠⎟⎟⎟⎟⎟⎟⎞=⎝⎜⎜⎜⎜⎜⎜⎛hnhn−1hn−2hn−3...h1⎠⎟⎟⎟⎟⎟⎟⎞
f(λ)=∣⎝⎜⎜⎜⎜⎛λ−a1−10...0−a2λ−1...0−a30λ...0...............−an−100...−1−an00...λ⎠⎟⎟⎟⎟⎞∣
根据第一行展开
f(λ)=(λ−a1)A1,1+(−a2)A1,2+(−a3)A1.3...+(−An)A1,n
其中
Ai,j表示A的代数余子式
显然所有余子式都是下三角矩阵,行列式很容易求得
那么我们得到了
f(λ)=λn−i=1∑naiλn−i
所谓特征多项式(刚好是特征根的形式的说)
如何计算
回到原问题,我们要求形如
H∗Am的式子
我们知道
f(A)=0,所以
Am=Ammodf(A)
做多项式取模我们得到了一个关于A的n-1次多项式,记为
∑i=0n−1ciAi
那么我们要求的就是
∑i=0n−1ciAiH的第m项
而
AiH=hi+n,也就是
∑i=0k−1cihi+k
那么我们先O(n^2)把
hn...h2∗n−1计算出来就好了
多项式取模可以用NTT也可以直接暴力
例题(bzoj4161)
直接O(k^2 log k)求出
xn%f(x)
注意特判k=1的情况(虽然数据没有)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=4e3+5,Mo=1e9+7;
int n,k,a[N],h[N],f[N],g[N],b[N],an[N];
void mul(int *a,int *b,int *c) {
fo(i,0,2*k-2) g[i]=0;
fo(i,0,k-1) fo(j,0,k-1) (g[i+j]+=(ll)a[i]*b[j]%Mo)%=Mo;
fd(i,2*k-2,k)
fd(j,k-1,0)
(g[i-k+j]-=(ll)g[i]*f[j]%Mo)%=Mo;
fo(i,0,k-1) c[i]=g[i];
}
int main() {
scanf("%d%d",&n,&k);
fo(i,1,k) scanf("%d",&a[i]);
fo(i,0,k-1) scanf("%d",&h[i]);
if (n<k) {printf("%d",(h[n]+Mo)%Mo);return 0;}
fo(i,0,k-1) f[i]=-a[k-i];f[k]=1;
if (k>1) b[1]=1;
else b[0]=-f[0];
an[0]=1;
for(n-=k-1;n;n>>=1) {
if (n&1) mul(an,b,an);
mul(b,b,b);
}
fo(i,k,2*k-1)
fo(j,1,k)
(h[i]+=(ll)h[i-j]*a[j]%Mo)%=Mo;
int ans=0;
fo(i,0,k-1) (ans+=(ll)an[i]*h[i+k-1]%Mo)%=Mo;
printf("%d\n",(ans+Mo)%Mo);
return 0;
}