题意:一个矩阵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;
}