HDU3306 Another kind of Fibonacci 矩阵
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - HDU3306
题意概括
A0=1,A1=1,AN=X*AN-1+Y*AN-2(N>=2).求SN,SN=A02+A12+…+An2.
题解
这题是用矩阵做的,一看(sou)就知道。
设si为前i项的答案。
如果要求第i项的ai那么是很简单的。
构建矩阵:
ai-1 ai
ai-2 0 y
ai-1 1 x
但是好像没用。
没错,的确没用。
我们从二次项考虑:
si =si-1+ai2
=si-1+(xai-1+yai-2)2
=si-1+x2ai-12+y2ai-22+2xyai-1ai-2
那么对于ak2形式的已经可以完成递推了。但是有一个棘手的东西,就是akak-1怎么完成递推?
我们继续推导:
akak-1=(xak-1+yak-2)ak-1
=xak-12+yak-1ak-2
我们发现ak-12和ak-1ak-2这两个其实可以按照之前推出来的推,而且不影响后面的。
于是,我们可以构建递推矩阵:
si ai2 ai-12 aiai-1
si-1 1 0 0 0
ai-12 x2 x2 1 x
ai-22 x2 y2 0 0
ai-1ai-2 2xy 2xy 0 y
意义:
si = si-1+x2ai-12+y2ai-22+2xyai-1ai-2
ai2 = x2ai-12+y2ai-22+2xyai-1ai-2
ai-12 = ai-12
aiai-1 = xai-12+ yai-1ai-2
那么原始矩阵的第一行就是
s1 a12 a02 a1a0
算出sn,就是把这个原始矩阵乘上n-1个递推矩阵。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cmath> #include <cstdlib> using namespace std; const int mod=10007,m=4; struct Mat{ int v[m][m]; Mat (){} Mat (int x){ (*this).set(x); } void print(){ for (int i=0;i<m;i++,puts("")) for (int j=0;j<m;j++) printf("%5d ",v[i][j]); puts(""); } void set(int x){ memset(v,0,sizeof v); if (x==1) for (int i=0;i<m;i++) v[i][i]=1; } Mat operator * (Mat x){ Mat ans(0); for (int i=0;i<m;i++) for (int j=0;j<m;j++) for (int k=0;k<m;k++) ans.v[i][j]=(ans.v[i][j]+v[i][k]*x.v[k][j])%mod; return ans; } void operator *= (Mat x){ (*this)=(*this)*x; } }M,Md; Mat MatPow(Mat x,int y){ Mat ans(1),now=x; while (y){ if (y&1) ans*=now; now*=now; y>>=1; } return ans; } int n,x,y; int main(){ while (~scanf("%d%d%d",&n,&x,&y)){ x%=mod,y%=mod; M.set(0); int newi[m]={2,1,1,1}; int newd[m][m]={{1 ,0 ,0,0}, {x*x%mod ,x*x%mod ,1,x}, {y*y%mod ,y*y%mod ,0,0}, {2*x*y%mod ,2*x*y%mod ,0,y}}; memcpy(M.v[0],newi,sizeof newi); memcpy(Md.v,newd,sizeof newd); Md=MatPow(Md,n-1); M*=Md; printf("%d\n",M.v[0][0]); } return 0; }