http://acm.hdu.edu.cn/showproblem.php?pid=5015
233 Matrix
1 110 02 223 47 163 7
234279972937
题意:一个表格第一行是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 10000007
using 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
我的下一篇博客有这道题的代码(那场比赛第一名的代码哦)