欢迎访问~原文出处——博客园-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-1 = 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;
}