• 洛谷 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
    ",ans.a[0][1]%mod);
        }   //输出 
        return 0;
    }
    

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

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

    ✐☎博主撰文不易,转载还请注明出处;若对本文有疑,请私信或在下方讨论中提出。O(∩_∩)O谢谢!☏

    ☃〔尽管小伙伴们肯定有千百种方式针对,但博主还是极其非常十分不要脸的把反对键吃掉辣!〕☃

    ✿『$At$ $last$:非常一(hu)本(shuo)正(ba)经(dao)的:博主很笨,请不要欺负他』✿✍

  • 相关阅读:
    排序
    git常用操作_分支合并_新建工程等
    ibatis 中调用存储过程
    IDEA试用期结束激活问题
    kafka本地工程的调用说明
    python yield 和 yield from用法总结
    ubuntu14.06 Lts开启ssh服务
    QT-1-环境搭建QT5.4.1&MinGW4.9.1
    虚拟机Ping不通主机解决
    CRC类(处理ITU表)
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/9879178.html
Copyright © 2020-2023  润新知