hdu1757 - A Simple Math Problem 矩阵快速幂

今天学矩阵突然开窍了

总结一下就是,矩阵乘法是没有实际意义的
(这里的意思是,在现实中找不到对应的东西,这是一个纯数学方法)
而且把原本普通算数可以做的事情转变成矩阵乘法是多此一举
比如说把点(x,y)左右平移10个单位,非常简单地可以想到变换后的坐标是(x+10,y)或者(x-10,y)。那为什么要用一个矩阵去表示这种变换呢?

因为矩阵乘法有结合律
说点实际例子。

  1. 假设不使用矩阵乘法,那么平移之后,又想要进行旋转,颠倒等等一系列操作。 你可能说,接着算呗

    那好,算完了 我给你第二个点,干刚才同样的事情 是不是又得从头到尾再算一遍?万一再给你一槽的点,是不是瞬间爆炸

    那如果我们用矩阵乘法呢? 我们知道,平移可以用矩阵表示 实际上,旋转,缩放,翻转等,都可以用矩阵表示 具体怎么构造这些矩阵,问度娘。

    那么假设原来的点坐标存放在矩阵x中(乘号右边一般都是列向量,乘号左边是则是行向量,这里假设变换矩阵都左乘)

    那么乘以平移矩阵A,得到AX 乘以旋转矩阵B,得到BAX…… …… 处理到最后,假设结果变成FEDBCAX,别忘了我们是从右往左算的

    但是他有结合律,这意味着我们可以改变计算顺序,先计算FEDBCA,再和X乘,得到的结果是相同的

    以后要对另一个坐标做相同变换,我们只需要用计算好的FEDBCA再去左乘它就可。

  2. 假设我们要干同一件事情,但是没有很快的方法直接算到终点(比如说这题,一个数和前面的数有关系,还有斐波拉契等类似的数列),等下讲解。

上面是矩阵乘法的个人认识,
说回题目

面对这些数列题目,在学过矩阵快速幂之后都可以用矩阵快速幂来做了
这些题目的特点就是递推公式可以用变换矩阵表示出来。

表示成变换矩阵之后,递推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(x1)f(x2)f(x3)f(x10)

右乘他之后,得到结果为
f(x)f(x1)f(x2)f(x9)

然后求出矩阵A的k-9次幂,一乘,是不是结果就出来了?
构造出的矩阵A如下,具体构造方法,我目前只会凑233

a01a11a21a31a9

矩阵是瞎写的,不要在意,我很喜欢用const 233

#include
#include
#include
#include
#include
#include
#include
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;i1;
    }
    ll* operator[](const int n)
    {
        return data[n];
    }
    Matrix operator* (const Matrix& ano) const
    {
        Matrix result(Rows,ano.Cols);
        for (int i=0;ifor (int j=0;jfor (int k=0;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<9-k][0]%m<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 <

你可能感兴趣的:(acm)