题意:一个矩阵M,M[i][j]=M[i-1][j]+M[i][j-1],就像杨辉三角一样。矩阵的第一行是0,233,2333,23333......,第一列由输入给出。求矩阵第n行m列的值模10000007后的数。
思路:矩阵快速幂。比赛的时候,隐约觉得这题是矩阵快速幂,但是不会写,因为以前没构造过矩阵。。学会这题真心涨姿势。
解题过程是这样的。除去第一行,把第一列看成是一个列向量v(由输入的那些数组成),v=(a1,a2,a3......)。然后我们可以往右推,看看每推一列,这个向量发生了什么,容易得到第二列(a1,a1+a2,a1+a2+a3......),可以把它看成是(b1,b2,b3......),继续往后推,还是有这样的规律。我们就可以构造如下方阵。
1 0 0 0 ...
1 1 0 0 ...
1 1 1 0 ...
1 1 1 1 ...
......
只要该元素参与求和,对应的方阵上的元素就是1,否则为0。
加上第一行以后,容易发现第一行每个元素是上一个元素乘以10再加上3,所以完整的列向量就是(a1,a2,a2,...,23,3)。完整的方阵是:
1 0 0 0 ... |10 1
1 1 0 0 ... |10 1
1 1 1 0 ... |10 1
1 1 1 1 ... |10 1
...... ... | ...
--------------------
0 0 0 0 ... |10 1
0 0 0 0 ... | 0 1
最后就是矩阵快速幂,求方阵的m次方,右乘上列向量,取结果的第n个数,完成。一句话总结:矩阵真是描述变换的好工具!
#include <iostream> #include <stdio.h> #include <cmath> #include <algorithm> #include <iomanip> #include <cstdlib> #include <string> #include <string.h> #include <vector> #include <queue> #include <stack> #include <map> #include <assert.h> #include <set> #include <ctype.h> #define ll long long #define max3(a,b,c) max(a,max(b,c)) using namespace std; const ll mod=1E7+7; struct mat{ ll v[16][16]; mat(){ memset(v,0,sizeof(v)); } }; mat mat_mul(mat a,mat b,int siz){ mat re; for(int i=0;i<siz;i++){ for(int j=0;j<siz;j++){ for(int k=0;k<siz;k++){ re.v[i][j]+=a.v[i][k]*b.v[k][j]; re.v[i][j]%=mod; } } } return re; } mat mat_pow(mat m,int n,int siz){ mat re; mat tmp=m; for(int i=0;i<siz;i++){ re.v[i][i]=1; } while(n){ if(n&1){ re=mat_mul(re,tmp,siz); } tmp=mat_mul(tmp,tmp,siz); n>>=1; } return re; } int main(){ int n,m; while(cin>>n>>m){ mat vec; mat a; for(int i=0;i<n;i++){ cin>>vec.v[i][0]; } vec.v[n][0]=23; vec.v[n+1][0]=3; for(int i=0;i<n;i++){ for(int j=0;j<=i;j++){ a.v[i][j]=1; } a.v[i][n]=10; a.v[i][n+1]=1; } a.v[n][n]=10; a.v[n][n+1]=1; a.v[n+1][n+1]=1; mat am=mat_pow(a,m,n+2); mat ans; ans=mat_mul(am,vec,n+2); cout<<ans.v[n-1][0]<<endl;; } return 0; }