zoukankan      html  css  js  c++  java
  • 洛谷 P4838 P哥破解密码 题解

    矩阵乘法 + 快速幂优化递推:

    看到这个题目我们不难想到递推,题干中说3个连续的A出现在序列中是不合法的,所以可以分为三种情况:

    (1):序列前只有一个A,如:BA,BBA,BABA。

    (2):序列前有两个A,如:BAA,BBAA,BABAA。

    (3):序列前没有A而是B,如:BB,AB,AABAAB。

    我们将这三种情况分别用 a1 , a2 , b 表示。

    // a1:1 1 2 4  7 13 24 44  81 149 274
    // a2:0 1 1 2  4  7 13 24  44  81 149 274
    // b :1 2 4 7 13 24 44 81 149 274
    

    我们不难发现在下一轮加A或加B时:a1可以转化为a2,a1和a2都可以转化为b,而b又可以转化为a1或再加一个b不变;

    故规律为:

    // f=a2;
    // a2=a1;
    // a1=b;
    // b=(a1+a2+f)%19260817;
    

    注意变量会相互覆盖掉,f是借来存值的;

    这是代码:

    #include<iostream>
    using namespace std;
    int m,n,i,a1,a2,b1,f;
    int main(){
    	cin>>m;
    	while(m--){
    		cin>>n;
    		a1=1;b1=1;a2=0;
    		for(i=2;i<=n;i++){
    			f=a2;a2=a1;a1=b1;
    			b1=(a1+a2+f)%19260817;
    		}
    		cout<<(a1+a2+b1)%19260817<<endl;
    	}
    	return 0;
    }
    

    但是!

    这题如果这么简单也就不会是蓝题了(其实上面那个代码也能拿80分的)。

    我们知道一般递推复杂度为o(n),而这题数据范围中n<=(10^9),还可以问十次,绝对会超时的!

    这里提供两种优化方案:

    (1):

    注:这是个打表不正经超有用骗满分暴力的方法:

    测评姬只给你一秒时限,但你可以在自己电脑上算很久都没问题。

    所以你可以在电脑上先算出从一开始每隔(10^7)位的三个递推数;当程序读入n时找到离n最近的递推数开始递推。

    简单来说就是你可以在数组中先存下(108)的三个递推数a1,a2和b,这样就能直接从(108)开始递推到n;估计能比正解还快不少!

    (2):

    注:其实这里才进入主题,上面可以当做都是准备工作。

    注:本蒟蒻也是今天才了解矩乘优化的QAQ,若有写的不好的地方各位大佬见谅。

    我们先来了解一下矩阵:

    矩阵就是一个二维方阵,矩阵A的第i行第j列可用A(i,j)来表示.

    一个n行m列的矩阵A就是这样:

    A(1,1) A(1,2) A(1,3) ... A(1,m)

    A(2,1) A(2,2) A(2,3) ... A(2,m)

    A(3,1) A(3,2) A(3,3) ... A(3,m)

    ...

    A(n,1) A(n,2) A(n,3) ... A(n,m)

    而矩阵乘法就是矩阵的一种运算

    那么矩阵乘法的对象就是两个矩阵A和B,

    注意:矩阵A的列数要与矩阵B的行数相等!

    那么运算的答案矩阵C的第i行第j列即为:

    C(i,j)=A(i,1)B(1,j)+A(i,2)B(2,j)+...+A(i,p)*B(p,j);

    如下:

    int jucheng(int n,int p,int m){
      memset(c,0,sizeof(c));
      for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)//p为矩阵A的列数与矩阵B的行数
          for (int k=1;k<=p;k++)      //枚举的所有递推数 
            c[i][j]+=a[i][k]*b[k][j]      //公式 
    }
    

    矩阵乘法不满足交换律,但满足结合律.

    AB!=BA

    (AB)C=A(BC)

    矩阵乘法也可以同余.

    而当我们将矩阵乘法用到递推中时:

    举一例:斐波那契数列:f[i]=f[i-1]+f[i-2].

    把它用矩阵乘法表示:

    //        A(1,1)  A(1,2)
    // [0 1]*                =[1 1]
    // A矩阵  A(2,1)  A(2,2)  C矩阵 
    //            B矩阵 
    

    不难根据公示得出B矩阵为

    0,1
    1,1
    

    故递推矩阵为:

    //                 0  1
    //[ f[i] f[i+1] ]*      =[ f[i+1] f[i]+f[i+1] ]=[ f[i+1] f[i+2] ]
    //                 1  1
    

    而本题中的递推矩阵为:

    1,1,1
    1,0,0
    0,1,0
    

    本题大意就是要将此矩阵反复乘n次。这就可以用快速幂了!

    下面现上代码:

    #include<bits/stdc++.h>
    using namespace std;
    struct ju{
    	long long s[3][3];
    };
    ju cheng(ju a,ju b){//矩乘函数: 
    	ju c;
    	for(int i=0;i<3;i++)for(int j=0;j<3;j++)c.s[i][j]=0;
    	for(int i=0;i<3;i++)
    	  for(int j=0;j<3;j++)
    	    for(int k=0;k<3;k++)
    	      c.s[i][j]+=a.s[i][k]*b.s[k][j],c.s[i][j]%=19260817;
    	return c;
    }
    ju fast(int n){//快速幂: 
    	ju c,d;
    	c.s[0][0]=c.s[0][1]=c.s[0][2]=c.s[1][0]=c.s[2][1]=1;
    	c.s[1][1]=c.s[1][2]=c.s[2][0]=c.s[2][2]=0;
    	if(n==1)return c;//
    	d=fast(n/2);//这个很关键 
    	if(n%2==0)return cheng(d,d);
    	return cheng(cheng(d,d),c);//多了一个1要再多乘1次 
    }
    void print(ju n){// 输出: 
    	cout<<(n.s[0][0]+n.s[0][1])%19260817<<endl;
    }
    int main(){
    	int m,n;
    	cin>>m;
    	while(m--){//一个一个算: 
    		cin>>n;
    	    print(fast(n));
    	}
    	return 0;//圆满 
    }
    

    码字挺累的(手残党,码了两小时了QAQ),大家点个赞再走吧。

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

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

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

  • 相关阅读:
    ZT 安卓手机的安全性 prepare for Q
    ZT pthread_cleanup_push()/pthread_cleanup_pop()的详解
    <Lord don’t move that mountain>
    C++浅拷贝和深拷贝的区别
    001 Python简介 输入输出
    016 可等待计时器对象.6
    016 EventDemo 5
    016 事件内核对象4
    016 内核对象的Signal状态3
    016 句柄2
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/9879218.html
Copyright © 2011-2022 走看看