[武汉加油] 【全国互虐】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;
}

猜你喜欢

转载自www.cnblogs.com/wljss/p/12300967.html