矩阵快速幂 HDU 233 Matrix

来源:互联网 发布:数据脱敏 编辑:程序博客网 时间:2024/06/03 20:23

http://acm.hdu.edu.cn/showproblem.php?pid=5015

233 Matrix

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2166    Accepted Submission(s): 1261


Problem Description
In our daily life we often use 233 to express our feelings. Actually, we may say 2333, 23333, or 233333 ... in the same meaning. And here is the question: Suppose we have a matrix called 233 matrix. In the first line, it would be 233, 2333, 23333... (it means a0,1 = 233,a0,2 = 2333,a0,3 = 23333...) Besides, in 233 matrix, we got ai,j = ai-1,j +ai,j-1( i,j ≠ 0). Now you have known a1,0,a2,0,...,an,0, could you tell me an,m in the 233 matrix?
 

Input
There are multiple test cases. Please process till EOF.

For each case, the first line contains two postive integers n,m(n ≤ 10,m ≤ 109). The second line contains n integers, a1,0,a2,0,...,an,0(0 ≤ ai,0 < 231).
 

Output
For each case, output an,m mod 10000007.
 

Sample Input
1 1
1
0 0
2 2
23 47 16
3 7
 

Sample Output
234
2799
72937


题意:一个表格第一行是0,233,2333,23333,233333······

输入n,m;n代表有n(n<=10)行(第1行到第n行的第一个数字给你)每一个格子的值等于前一个格子和上一个格子的和(除了已经给出值的格子);m代表列数;求第n行第m列的值?

思路;因为m是10^9非常大,由上一状态得到下一状态用矩阵表示出来,用快速幂求出。

先假设第0行都为0;先求竖着的和:

0  0  0   0     0   0     0    0

1  1  1   1     1   1     1   1

1  2  3   4     5   6     7    8

1  3  6  10   15  21   28  36

1  4 10  20   35  56   84  120

1  5 15  35  70 126  210 330

从这个例子中观察。第0行先不管,先看第一列1 1 1 1 1这是初始数据我用a[i][j]代表每一个数字第i行第j列;

a[1][1]=a[1][0];

a[2][1]=a[2][0]+a[1][0];

a[3][1]=a[3][0]+a[2][0]+a[1][0];

可以发现a[i][j]是a[1][j-1]+a[2][j-1]+……+a[i][j-1];而且每一个数字都满足这一运算性质。所以把这个运算方法存到矩阵中

(系数矩阵)

第j列表示为(这是n=5的矩阵)(m是要乘的次数)

s5. s4. s3. s2. s1.       1  0   0   0   0         s5  s4  s3  s2  s1

0    0    0    0   0          1   1  0   0   0            0    0    0    0   0 

0    0    0    0   0     *    1   1  1   0   0     =   0    0    0    0   0  

0    0    0    0   0          1   1   1   1  0           0    0    0    0   0  

0    0    0    0   0          1   1   1   1   1          0    0    0    0   0

s后面加点的代表前一列的数字 ,s后没点的代表运算后的矩阵

矩阵运算法则是结果的第i行第j列的那个数字=(第一个矩阵第i行的数字分别乘以第二个矩阵第j列的数字)之和;

s1=0*s5.+0*s4.+0*s3.+0*s2.+1*s1.;

s2=0*s5.+0*s4.+0*s3.+1*s2.+1*s1.;

s3=0*s5.+0*s4.+1*s3.+1*s2.+1*s1.;

s4=0*s5.+1*s4.+1*s3.+1*s2.+1*s1.;

s5=1*s5.+1*s4.+1*s3.+1*s2.+1*s1.;

中间的那个矩阵是上式的系数(系数矩阵)

结果如下:我只给你们模拟两次吧0.0(剩下自己想)

1    1    1    1   1       1   0    0   0   0          5    4    3    2    1

0    0    0    0   0        1   1    0   0   0          0    0    0    0   0  

0    0    0    0   0    *   1   1    1   0   0     =   0    0    0    0   0  

0    0    0    0   0        1   1    1   1   0           0    0    0    0   0  

0    0    0    0   0        1   1    1   1   1           0    0    0    0   0 


5    4    3    2   1        1   0    0   0   0          15  10   6    3   1

0    0    0    0   0        1   1    0   0   0           0    0    0    0   0  

0    0    0    0   0    *   1   1    1   0   0     =    0    0    0    0   0  

0    0    0    0   0        1   1    1   1   0           0    0    0    0   0  

0    0    0    0   0        1   1    1   1   1            0    0    0    0   0 

结果矩阵的a[0][0]=15就是m=2时所要的答案。

如果我要让m=1000;是不是你要求第一千列的数字是什么。

所以要乘1000一千次这个系数矩阵,如果m非常大一次一次乘肯定超时,所以要用快速幂。

看到这就成功了一半0.0了.

刚才算的是竖着的那一排,现在要算横着的那一排,思想还是一样的,不过只多了一点233,2333,23333,233333……这个东西也要用系数矩阵表示;

举个栗子:n=3时,m是要乘的次数;这个跟上面的差不多。

注意(这些s1,s2,s3也是一列一列求的)乘m次后 s3是n=3的结果

(n如果=10的话这个矩阵要开[12][12],因为多了a 和3)代码中有n=n+2;   a表示那个数字是几,si代表前一列(前i项的和)

s3. s2. s1.  a.  3.      1   0   0   0   0          s3  s2  s1  a  3

0    0    0    0   0        1   1   0   0   0          0    0    0    0   0 

0    0    0    0   0    *  1   1   1   0   0     =   0    0    0    0   0  

0    0    0    0   0       10 10 10 10  0          0    0    0    0   0  

0    0    0    0   0       1   1   1   1   1          0    0    0    0   0

a=a.*10+3.下一个a和上一个a的关系。中间的矩阵就是系数矩阵.

0    0    0   23  3      1   0    0   0   0      233  233  233  233  3

0    0    0    0   0      1   1    0   0   0       0    0    0    0      0  

0    0    0    0   0    * 1   1   1   0   0   =  0    0    0    0      0  

0    0    0    0   0      10 10 10 10  0       0    0    0    0      0  

0    0    0    0   0      1   1    1   1   1       0    0    0    0      0 

栗子你们继续往下乘几次你就懂了。

最后a[0][0]表示结果。

输出(上面求出来的两个值的和)%10000007

附上我自己打的AC代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<map>#include<string>#define LL long long int#define inf 0x3f3f3f3f#define N 100010#define mod 10000007using namespace std;int n,m;struct matrix{    LL a[15][15];    matrix()    {        memset(a,0,sizeof (a));    }} b,c,d,e,f;//这些b,c,d,e,f表示不同的矩阵;matrix operator *(matrix p,matrix q)//这些东西刚开始我也不理解,想了很//久才明白,operator * 表示自己构造的乘法运算(矩阵乘法运算){    memset(&d,0,sizeof d);    for(int i=0; i<n; i++)        for(int j=0; j<n; j++)            for(int k=0; k<n; k++)                d.a[i][j]=(d.a[i][j]+p.a[i][k]*q.a[k][j])%mod;    return d;//i,j表示第i行第j列 第k个;自己模拟一下。}int main(){    while(~scanf("%d%d",&n,&m))    {        memset(&b,0,sizeof b);        memset(&c,0,sizeof c);        memset(&d,0,sizeof d);        memset(&e,0,sizeof e);        memset(&f,0,sizeof f);        for(int i=n-1; i>=0; i--)            scanf("%I64d",&b.a[0][i]);        for(int i=0; i<n; i++)            for(int j=0; j<n; j++)                if(i>=j)                    c.a[i][j]=1;        int fl=m;        while(fl)        {            if(fl&1)b= b * c;            c= c * c;            fl/=2;        }//快速幂;注意b,c表示矩阵。*表示的是矩阵乘法。//        printf("%I64d",b.a[0][0]);           n=n+2;        f.a[0][n-1]=3;        f.a[0][n-2]=23;        for(int i=0; i<n; i++)            for(int j=0; j<n; j++)                if(i>=j)                {                    if(i!=n-2)                        e.a[i][j]=1;                    else                        e.a[i][j]=10;                }        fl=m;        while(fl)        {            if(fl&1)f= f * e;            e= e * e;            fl/=2;        }//           printf(" **   %I64d\n",f.a[0][0]);           printf("%I64d\n",(f.a[0][0]+b.a[0][0])%mod);    }}

看不懂也没关系,附上一道另一道简单的矩阵快速幂做做吧https://oj.ejq.me/problem/26

我的下一篇博客有这道题的代码(那场比赛第一名的代码哦)




1 0