您的位置:首页 > 其它

hdu1757 - A Simple Math Problem 矩阵快速幂

2015-04-26 12:02 375 查看
今天学矩阵突然开窍了

总结一下就是,矩阵乘法是没有实际意义的

(这里的意思是,在现实中找不到对应的东西,这是一个纯数学方法)

而且把原本普通算数可以做的事情转变成矩阵乘法是多此一举

比如说把点(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,使得一个列向量

⎛⎝⎜⎜⎜⎜⎜⎜f(x−1)f(x−2)f(x−3)……f(x−10)⎞⎠⎟⎟⎟⎟⎟⎟

右乘他之后,得到结果为

⎛⎝⎜⎜⎜⎜⎜⎜f(x)f(x−1)f(x−2)……f(x−9)⎞⎠⎟⎟⎟⎟⎟⎟
然后求出矩阵A的k-9次幂,一乘,是不是结果就出来了?

构造出的矩阵A如下,具体构造方法,我目前只会凑233

⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜a01a11a21a31…………a9⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟
矩阵是瞎写的,不要在意,我很喜欢用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
;
}
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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: