• 首师大附中互测题:LJX的校园:入学典礼【C003】


    【C003】LJX的校园:入学典礼【难度C】——————————————————————————————————————————————————————————————————————————————————————

    【题目要求】

     LJX上中学啦!他与YSM,YSF,WHT,LTJ等人都是校友。今天,是他人生中“溺亡”的一天。今天,他要向同学们证明他的数学很“乐呵”。 于是,刚学会简单的A+B问题的他,在课上,向冤  家对头 斯沃琪 挑战 QAQ,斯沃琪 队有YZM,SJY,ZZQ等人。而LJX队有他的好朋(ji)友:YSM,YSF,WHT,LTJ,LZH等人,实力不弱小觑。有这么一道题:

     给定正整数N,M,要求计算1,2,……,N连接起来(1234567891011……N)mod M的值。

     “新兵”LJX想了想,要是M是3,或者9,他一定会。但是M什么都可以,只要小于INF。“预约”了各种方法以后,费劲脑筋想不出来。于是,右转向了(呵呵)他的(ZHU)队友们。可他们已经跑了 TAT。jue ruo LJX找到了你,他跪求你编一个程序帮他解决问题。否则,他将在毕业典礼上“锻炼”,并且被可怕的斯沃琪虐残 QAQ

    【输入要求】

     * 一行:正整数N,M。

    【输入示例】

    输入样例1
    13 13
    输入样例2
    12345678910 1000000000
    

    【输出要求】

     * 一行:按要求输出

    【输出示例】

    输出样例1
    4
    输出样例2
    345678910
    

    【其它要求】

     N<=10^18
     M<=10^9
     这数据是在坑LJX呀

    【试题分析】对于连线段树都用不熟练的“键人”蒟蒻(不是魔芋)来说,看到其它要求瞬间蒙逼,最后花了10^1000000000纳秒终于发现了这道题简直就是连改都没改的矩阵快速幂,于是直接上模板。

                     矩阵快速幂大家都应该很熟悉了下面我来讲一下它的基本原理:两矩阵相乘,一般的算法(QAQ)的复杂度是O(N^3)。如果求一次矩阵的M次幂,按朴素的写法就O(N^3*M)。既然是求幂,不免想到快速幂取模的算法,有了快速幂取模的,a^b %m 的复杂度可以降到O(logb)。如果矩阵相乘是不是也可以实现O(N^3 * logM)的时间复杂度呢?答案是肯定的。我们可以

                     把n个矩阵进行两两分组,比如:A*A*A*A*A*A  =>  (A*A)*(A*A)*(A*A)这样变的好处是,你只需要计算一次A*A,然后将结果(A*A)连乘自己两次就能得到A^6,即(A*A)^3=A^6。算一下发现这次一共乘了3次,少于原来的5次。其实大家还可以取A^3作为一个基本单位。原理都一样:利用矩阵乘法的结合律,来减少重复计算的次数。以上都是取一个具体的数来作为最小单位的长度,这样做虽然能够改进效率,但缺陷也是很明显的,取个极限的例子(可能有点不恰当,但基本能说明问题),当n无穷大的时候,你现在所取的长度其实和1没什么区别。所以就需要我们找到一种与n增长速度”相适应“的”单位长度“,那这个长度到底怎么去取呢???这点是我们要思考的问题。有了以上的知识,我们现在再来看看,到底怎么迅速地求得矩阵的N次幂。我们可以进行离散化(其实我也不懂)下面是我转载的
                    大家首先要认识到这一点:任何一个整数N,都能用二进制来表示。。这点大家都应该知道,但其中的内涵真的很深很深(这点笔者感触很深,在文章的最后,我将谈谈我对的感想)!!
                    计算机处理的是离散的信息,都是以0,1来作为信号的处理的。可想而知二进制在计算机上起着举足轻重的地位。它能将模拟信号转化成数字信号,将原来连续的实际模型,用一个离散的算法模型来解决。  好了,扯得有点多了,不过相信这写对下面的讲解还是有用的。
                    回头看看矩阵的快速幂问题,我们是不是也能把它离散化呢?比如A^19  =>  (A^16)*(A^2)*(A^1),显然采取这样的方式计算时因子数将是log(n)级别的(原来的因子数是n),不仅这样,因子间也是存在某种联系的,比如A^4能通过(A^2)*(A^2)得到,A^8又能通过(A^4)*(A^4)得到,这点也充分利用了现有的结果作为有利条件。下面举个例子进行说明:
                   现在要求A^156,而156(10)=10011100(2) 也就有A^156=>(A^4)*(A^8)*(A^16)*(A^128)  考虑到因子间的联系,我们从二进制10011100中的最右端开始计算到最左端。细节就说到这。

    【代码】

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    
    #define MAXN 4
    
    using namespace std;
    
    typedef long long int LL;
    
    int mod;
    
    struct matrix
    {
        LL p[MAXN][MAXN];
    }ans,tmp;
    
    matrix operator*(matrix a,matrix b)
    {
        matrix c;
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
            {
                c.p[i][j]=0;
                for(int k=1;k<=3;k++)
                    c.p[i][j]=(c.p[i][j]+((a.p[i][k]%mod)*(b.p[k][j]%mod))%mod)%mod;
            }
        return c;
    }
    
    void cal(LL t,LL last) 
    {
        memset(tmp.p,0,sizeof(tmp.p));
        tmp.p[1][1]=t;
        tmp.p[2][1]=tmp.p[3][1]=tmp.p[2][2]=tmp.p[3][2]=tmp.p[3][3]=1;
        LL y=last-t/10+1;
        while(y)
        {
            if(y&1) ans=ans*tmp;
            tmp=tmp*tmp;
            y>>=1;
        }
    }
    
    int main()
    {
        for(int i=1;i<=3;i++)
            ans.p[i][i]=1;
        LL n;
        scanf("%lld%lld",&n,&mod);
        LL t=10;
        while(n>=t)
        {
            cal(t,t-1);
            t*=10;
        }
        cal(t,n);
        printf("%lld
    ",ans.p[3][1]);
        return 0;
    }
    

     我的感想:艰苦的学完快速幂之后,每次比赛把那么一大长串的思想重新想一遍肯定是不行的,所以我们一定要背模板!背模板!背模板!(重要的事情说三遍)要做到拿来就能写。

  • 相关阅读:
    gcc 编译器常用的命令行参数一览
    linux下源代码分析和阅读工具比较
    Linux系统——C/C++开发工具及环境搭建
    GDB调试——经验总结
    gdb调试的艺术——Debug技巧
    命令__cp、scp(Secure Copy)
    常用shell脚本命令
    命令__查找、替换、删除
    UltraEdit 删除空行
    命令__shell数字-字符串比较
  • 原文地址:https://www.cnblogs.com/lijiaxin-blog-cpp/p/5605849.html
Copyright © 2020-2023  润新知