矩阵与数列优化
Posted czktransformers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了矩阵与数列优化相关的知识,希望对你有一定的参考价值。
心怀滕王高阁,手握物理周测······心绪阑干。校服不耐五更寒。
请看这样一道小题:原题地址https://www.luogu.org/problemnew/show/P1939;
题目描述
a[1]=a[2]=a[3]=1
a[x]=a[x-3]+a[x-1] (x>3)
求a数列的第n项对1000000007(10^9+7)取余的值。
其中n高达2*10^9。
于是暴力挂了,只能另寻它道。
这时,学习后几乎从未使用过的技能矩阵乘法终于亮出了刀锋。
矩阵乘法与递推数列的求和有何关系?
针对此题,我们可以视数列为1x3矩阵{a[x-3],a[x-2],a[x-1]},由此矩阵乘以某个矩阵后得到新的1x3矩阵{a[x-2],a[x-1],a[x]}。
又因为a[x]=a[x-1]+a[x-3],利用矩阵乘法性质,构造出了3x3矩阵
0 0 1
1 0 0
0 1 1
直接利用矩阵快速幂
设原矩阵为a,构造矩阵为b,所求矩阵为c,则k>=3时,有c=a*(b^(k-3));
以此,得以AC。
不过输出时一定记得取模!!!我就这样挂掉了第一遍,只得十分。
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #define ll long long using namespace std; const ll modd=1e9+7; ll n,k,T; struct zz { ll x[4][4]; int n,m; zz operator * (zz b) const { zz mid; mid.n=n; mid.m=b.m; memset(mid.x,0,sizeof(mid.x)); for(int i=1;i<=mid.n;i++) for(int j=1;j<=mid.m;j++) for(int k=1;k<=m;k++) mid.x[i][j]=mid.x[i][j]%modd+x[i][k]*b.x[k][j]%modd; return mid; } }dw; zz ksm(zz a,ll k) { zz c;c.n=3;c.m=3; memset(c.x,0,sizeof(c.x)); c.x[1][1]=c.x[2][2]=c.x[3][3]=1; while(k) { if(k&1)c=c*a; a=a*a; k>>=1; } return c; } int main() { scanf("%lld",&T); zz s,t; s.n=s.m=3;dw.n=1;dw.m=3; s.x[1][1]=0;s.x[1][2]=0;s.x[1][3]=1; s.x[2][1]=1;s.x[2][2]=0;s.x[2][3]=0; s.x[3][1]=0;s.x[3][2]=1;s.x[3][3]=1; dw.x[1][1]=dw.x[1][2]=dw.x[1][3]=1; while(T--) { scanf("%lld",&k); if(k<=3){printf("1 ");continue;} t=ksm(s,k-3); t=dw*t; printf("%lld ",t.x[1][3]%modd); } return 0; }
同样滴,对于斐波那契数列,矩阵乘法同样是相当适用。
F(0)=1 , F(1)=1 , F(n)=F(n-1)+F(n-2)
矩阵变为2x2
0 1
1 1
对于像洛谷1306这种题,最后一组数据甚至卡爆了大部分题解,唯有矩乘方能刷过(打表这种面向数据编程的方法不算)
总结一句,对于递推数列求项数,矩乘实在是不二之选,构造也十分轻松。
以上是关于矩阵与数列优化的主要内容,如果未能解决你的问题,请参考以下文章