洛谷 P4910 帕秋莉的手环 矩阵乘法+快速幂详解

矩阵快速幂解法:

这是一个类似斐波那契数列的矩乘快速幂,所以推荐大家先做一下下列题目:(会了,差不多就是多倍经验题了

注:如果你不会矩阵乘法,可以了解一下P3390的题解

P1939 【模板】矩阵加速(数列)

P3390 【模板】矩阵快速幂

P1306 斐波那契公约数

P1962 斐波那契数列

P4838 P哥破解密码

由题意可得:相邻两个珠子中必有金属性珠子。这其实就可以理解为不能有连续的两个木属性珠子。这样一看,此题就和P4838 P哥破解密码差不多了。只不过这题是个2*2矩阵乘法

进入正文:

我们先一次将1~n中每一个珠子的情况枚举


//                n=1 n=2 n=3 n=4 n=5 n=6 .......
//可放金属性珠子:  1   2   3   5   8   13 .......
//可放木属性珠子:  1   1   2   3   5   8  .......

不难发现这就是一个斐波那契数列的递推

但是:这是一个手环!

所以第一个珠子与最后一个手环是相连的,他们会互相影响!

不过他们只会影响对方而不会影响其他珠子,我们可以将第一颗珠子选金属性与木属性这两种情况分开:

//第一颗珠子为金属性: 若 n=5 
//        1   2   3   4   n  .......
//金属性: 1   1   2   3   5  .......
//木属性: 0   1   1   2   3  .......

//第一颗珠子为木属性: n=5 
//        1   2   3   4   n  .......
//金属性: 0   1   1   2   3  .......
//木属性: 1   0   1   1   0  .......
                //最后一颗不能为木! 
//两种情况加起来就是样例1的解了 

所以此题就是求斐波那契数列第n项第n-1项的两倍

然后就可矩阵快速幂了!递推矩阵如下

//                     1 1
//                     0 1

不过我们当然不能止步于此:

因为还有一种更无脑有效的方法:

既然矩阵可以快速幂,那么说明每两个递推数(即答案)之间的递推矩阵是一样的!

所以我们可以先手算两组结果,然后直接推出递推矩阵:


//  3 4  乘  递推矩阵  =  4 7

//  解上述方程得递推矩阵为: 
//             0  1
//  3 4  乘            =  4 7
//             1  1

下面上代码:

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007//简化一下 
struct ju{
    long long a[2][2];//不用long long只有8分哦(亲测QAQ)  
    ju operator *(const ju &x){
        ju res;//这里需要另外建个矩阵存答案 
        memset(res.a,0,sizeof(res.a));
        for(int i=0;i<2;i++)
          for(int j=0;j<2;j++)
            for(int k=0;k<2;k++)
              res.a[i][j]=(res.a[i][j]+a[i][k]%mod*(x.a[k][j])%mod)%mod;
        return res;
    }         //重载运算符,(可以写成函数) 
}base,ans;//两个基本矩阵 
int main(){
    long long n,t;
    scanf("%lld",&t);//只有t就不写快读了 
    while(t--){
        scanf("%lld",&n);n--;//n要减一,不然会错 QAQ 
        base.a[0][0]=0;base.a[0][1]=1;base.a[1][0]=1;base.a[1][1]=1;
        ans.a[0][0]=2;ans.a[0][1]=1;ans.a[1][0]=0;ans.a[1][1]=0;//初始化 
        while(n){//快速幂,(也可以写成函数) 
            if(n%2==1) ans=ans*base;
            base=base*base;
            n/=2;
        }
        printf("%lld\n",ans.a[0][1]%mod);
    }   //输出 
    return 0;
}

代码中ans的初始化已经是n=1时的答案了所以n要减一。

啊,写题解好累啊,是我太蒟了吗。。

猜你喜欢

转载自www.cnblogs.com/812-xiao-wen/p/9879178.html