今天学矩阵突然开窍了
总结一下就是,矩阵乘法是没有实际意义的
(这里的意思是,在现实中找不到对应的东西,这是一个纯数学方法)
而且把原本普通算数可以做的事情转变成矩阵乘法是多此一举
比如说把点(x,y)左右平移10个单位,非常简单地可以想到变换后的坐标是(x+10,y)或者(x-10,y)。那为什么要用一个矩阵去表示这种变换呢?
因为矩阵乘法有结合律!
说点实际例子。
假设不使用矩阵乘法,那么平移之后,又想要进行旋转,颠倒等等一系列操作。 你可能说,接着算呗
那好,算完了 我给你第二个点,干刚才同样的事情 是不是又得从头到尾再算一遍?万一再给你一槽的点,是不是瞬间爆炸
那如果我们用矩阵乘法呢? 我们知道,平移可以用矩阵表示 实际上,旋转,缩放,翻转等,都可以用矩阵表示 具体怎么构造这些矩阵,问度娘。
那么假设原来的点坐标存放在矩阵x中(乘号右边一般都是列向量,乘号左边是则是行向量,这里假设变换矩阵都左乘)
那么乘以平移矩阵A,得到AX 乘以旋转矩阵B,得到BAX…… …… 处理到最后,假设结果变成FEDBCAX,别忘了我们是从右往左算的
但是他有结合律,这意味着我们可以改变计算顺序,先计算FEDBCA,再和X乘,得到的结果是相同的
以后要对另一个坐标做相同变换,我们只需要用计算好的FEDBCA再去左乘它就可。
- 假设我们要干同一件事情,但是没有很快的方法直接算到终点(比如说这题,一个数和前面的数有关系,还有斐波拉契等类似的数列),等下讲解。
上面是矩阵乘法的个人认识,
说回题目
面对这些数列题目,在学过矩阵快速幂之后都可以用矩阵快速幂来做了
这些题目的特点就是递推公式可以用变换矩阵表示出来。
表示成变换矩阵之后,递推n次其实就是左乘或者右乘 变换矩阵的n次幂
对于一个整形数,有如下快速幂黑科技,具体就不说了
int QuickPow(int B ,int k)
{
int ans(1);
while (k)
{
if (k&1)
ans=ans*B%MOD;
k>>=1;
B = B*B %MOD ;
}
return ans;
}
快速幂的计算方法,是需要结合律的
矩阵乘法正好满足结合律
然后……就可以套上快速幂,变身矩阵快速幂
简直不要太神奇
再说回题目,题目的递推公式是
If x < 10 f(x) = x.
If x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10);
那么我们做一个变换矩阵A,使得一个列向量
右乘他之后,得到结果为
然后求出矩阵A的k-9次幂,一乘,是不是结果就出来了?
构造出的矩阵A如下,具体构造方法,我目前只会凑233
矩阵是瞎写的,不要在意,我很喜欢用const 233
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
#define ll long long
//Template mat
//Interface here
#define MULMOD
//Use Matrix(int row,int cow) to define a new EMPTY matrix
//Use Matrix(int n) to define a n*n identity matrix
//Use define MULMOD and set MOD to ENABLE MOD
#define MAXROWS 10
#define MAXCOLS 10
#ifdef MULMOD
int MOD;
#endif
//Interface End
struct Matrix
{
int Rows,Cols;
ll data[MAXROWS][MAXCOLS];
void clear()
{
memset(data,0,sizeof(data));
}
Matrix (int n,int m)
:Rows(n),Cols(m)
{
clear();
}
Matrix (int n)
:Rows(n),Cols(n)
{
clear();
for (int i=0;i<n;i++)
data[i][i]=1;
}
ll* operator[](const int n)
{
return data[n];
}
Matrix operator* (const Matrix& ano) const
{
Matrix result(Rows,ano.Cols);
for (int i=0;i<Rows;i++)
for (int j=0;j<ano.Cols;j++)
for (int k=0;k<Cols;k++)
{
#ifdef MULMOD
result[i][j] += data[i][k] * ano.data[k][j] % MOD;
result[i][j]%=MOD;
#else
result[i][j] += data[i][k] * ano.data[k][j];
#endif
}
return result;
}
};
Matrix QuickMatrixPow(Matrix to ,int k)
{
Matrix ans(to.Rows);
for (int i=0;i<10;i++)
ans[i][i]=1;
while (k)
{
if (k&1)
ans=ans*to;
k>>=1;
to = to*to;
}
return ans;
}
//Template mat end
int main()
{
cin.sync_with_stdio(false);
Matrix a(10,10);
Matrix f0(10,1);
int k,m;
while (cin>>k>>m)
{
a.clear();
f0.clear();
MOD=m;
for (int i=0;i<10;i++) //构造左乘矩阵
{
cin>>a[0][i];
f0[i][0]=9-i;
}
for (int i=0;i<9;i++)
a[i+1][i]=1;
if (k<10)
{
cout<<f0[9-k][0]%m<<endl;
continue;
}
//左乘一次得f(10) f(9) f(8) ……f(1)的矩阵
//两次 f(11) f(10) …… f(2)
//所以要计算f(k),需要左乘 k-9次
//计算左乘的k-9个矩阵
cout <<( QuickMatrixPow(a,k-9) * f0)[0][0] %m <<endl;
}
}