[武汉加油] 【全国互虐】Fibonacci矩阵
题意:
给定一个\(k\)维的\(n^k\)的超立方体,超立方体的元素\(A(i1,i2,...,ik)\)的值为\(f(i1+i2+...+ik-k+1)\),f为斐波那契数列
求该超立方体的所有元素和
\(1 \le n,k \le 10^9\)
输入样例
3
2 2
4 1
1 3
输出样例
5
7
1
题解
自己好长时间没写题解了,自己好懒啊。
假如k=1,那么就是答案就是斐波那契数列的前缀和。
这里有一个神奇的结论 :设\(f[i]\)为斐波那契数列的第\(i\)项,则\(\displaystyle \sum_{i=1}^{n}f[i]=f[n+2]-f[2]\)
我们用矩阵快速幂就能解决啦。
假如k<=100,我们就需要用到一个神奇的结论了:当我们做完一维后,可以把这维看成成一个点,权值是这维的和。
我们先来考虑只有两维的情况,我们求的就是\(\displaystyle \sum_{i=1}^{n}(\sum_{j=1}^{n}f[i+j])\),我们发现我们求完了一维后,将这一维压缩成前缀和后,我们会面临相同的子问题。推广到k维依然适合,所以我们可以用k次快速幂来解决。
假如k更大,我们可以考虑维之间的转化,假如我们求出了转移到下一维的矩阵B,我们用初始矩阵\(A\)乘上\(B^k\)就能求出来答案。
那B怎么求呢?我们发现维度的转换只用求出来这一维的前缀和就行了,而刚刚的\(\displaystyle \sum_{i=1}^{n}f[i]=f[n+2]-f[2]\)在更高维上显然依然适用,将公式套到矩阵上就可以啦。
#include<iostream>
#include<cstring>
#include<cstdio>
#define LL long long
using namespace std;
const int mod=1e9+7;
LL n,k,T;
struct ju
{
LL v[3][3];
friend ju operator *(const ju &a,const ju &b)
{
ju c;memset(c.v,0,sizeof(c.v));
for(int i=1;i<=2;++i)
for(int j=1;j<=2;++j)
for(int k=1;k<=2;++k)
(c.v[i][j]+=a.v[i][k]*b.v[k][j])%=mod;
return c;
}
friend ju operator -(const ju &a,const ju &b)
{
ju c;memset(c.v,0,sizeof(c.v));
for(int i=1;i<=2;++i)
for(int j=1;j<=2;++j)
c.v[i][j]=(a.v[i][j]-b.v[i][j]+mod)%mod;
return c;
}
}A,B;
void YYCH()
{
A.v[1][1]=1;A.v[1][2]=0;
B.v[1][1]=1;B.v[1][2]=1;B.v[2][1]=1;
}
ju ksm(ju a,LL b)
{
ju res;memset(res.v,0,sizeof(res.v));
res.v[1][1]=res.v[2][2]=1;
for(;b;b>>=1,a=a*a)
if(b&1)res=res*a;
return res;
}
int main()
{
YYCH();
cin>>T;
while(T--)
{
scanf("%lld%lld",&n,&k);
printf("%lld\n",(A*ksm(ksm(B,n+1)-B,k)).v[1][1]);
}
return 0;
}