【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。“预约”了各种方法以后,费劲脑筋想不出来。于是,右转向了(呵呵)他的(队友们。可他们已经跑了 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\n",ans.p[3][1]);
return 0;
}

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

04-16 21:22