【矩阵】矩阵初级

百度百科

Definition

  矩阵是一个按照长方阵列排列的复数或实数集合。

  描述一个矩阵的形状使用两个参数,为矩阵的行数和列数。在代码中是用r和c体现。

  约定在描述矩阵时,行在前列在后,例如一个n*m的矩阵意味着一个n行m列的矩阵。

  两个矩阵相同,当且仅当他们行、列数相同并且元素完全相同。

  两个矩阵形状相同,当且仅当他们行、列数相同。

operation algorithm

 addition

  两个矩阵能够进行加法,当且仅当两个矩阵形状相同。

  两个矩阵进行加法,返回一个矩阵,矩阵行、列数与原矩阵相同,对应位置上的值为两个矩阵对应位置上值的和。例如:

       

 subtraction

  类比两个矩阵进行加法,两个矩阵能进行减法,当且仅当他们形状相同。

  两个矩阵进行减法,返回一个矩阵,矩阵行、列数与原矩阵相同,对应位置上的值为两个矩阵对应位置上值的差。例如:

       

 multiplication

  两个矩阵A,B能进行A*B的矩阵乘法,当且仅当A的列数等于B的行数。

  注意,A*B ≠ B*A

  下面约定矩阵A为乘号前的矩阵,矩阵B为乘号后的矩阵。

  两个矩阵A,B进行乘法A*B,返回一个矩阵C,矩阵行数为A的行数;矩阵列数为B的列数。矩阵第i行第j列的值C(i,j)被定义如下:

    C(i,j)A.ck=1 A(i,k)*B(k,j)   。其中A.c代表A的列数。

  例如:

       

 division

  两个矩阵做除法A/B,求出B的逆矩阵B-1,则等价于A*B-1。求逆矩阵的方法过于复杂不表。

 

 

 lows

   矩阵加、减法具有交换律,结合率。

   矩阵乘法具有结合率。即(A*B)*C=A*(B*C)。不具有交换律。即A*B*C!=A*C*B。

矩阵快速幂

  方阵:一个矩阵时方阵当且仅当它的行数等于列数

  主对角线:一个方阵的主对角线被定义为从左上角连接到右下角的对角线。

  单位矩阵:一个方阵是单位矩阵当且仅当它有且仅有主对角线上元素的值为1,其他值为0。

  性质:任何一个矩阵乘上一个能和他相乘的单位矩阵,得到的矩阵是它本身。

  矩阵快速幂:

    一个矩阵能进行矩阵快速幂当且仅当它是一个方阵。

    矩阵进行快速幂与普通快速幂相同。必须使用循环形式。因为使用递归可能会占用较多资源。

矩阵优化类递推题目

  需要矩阵优化的递推题目一般递推式中,该位置i的值一般前面第i-k项有关,其中k为若干个常数。例如fibnacci数列:fi=fi-1+fi-2

  这样的题目可以将fi到fi+x连续组成一个矩阵。其中x为k的最大值。设W为一个矩阵,解方程|fi,fi+1,…,fi+x|*W=|fi+1,fi+2,…,fi+x+1|。

  解出W是一个常量矩阵,这样数学归纳易证f1*Wn-1=fn,可以求出第n项的值。

Codes

  下面是封装了矩阵加、乘两则运算的代码

struct M {
    static const int Maxsize = 10;
    static const int MODNUM = 1000000007;
    int r,c;
    ll mat[Maxsize][Maxsize];
    void clear() {memset(mat,0,sizeof mat);r=c=0;}
    bool can_mul(const M &_a,const M _b) {
        return _a.c==_b.r;
    }
    bool can_add(const M &_a,const M &_b) {
        return (_a.r==_b.r) and (_a.c==_b.c);
    }
};
M operator+(const M &_a,const M &_b) {
    M _ans;_ans.clear();
    for(rg int i=1;i<=_a.r;++i) for(rg int j=1;j<=_a.c;++j) _ans.mat[i][j]=(_a.mat[i][j]+_b.mat[i][j])%M::MODNUM;
    _ans.r=_a.r;_ans.c=_a.c;
    return _ans;
}
M operator*(const M &_a,const M &_b) {
    M _ans;_ans.clear();
    for(rg int i=1;i<=_a.r;++i) 
        for(rg int j=1;j<=_b.c;++j)
            for(rg int k=1;k<=_a.c;++k)
                _ans.mat[i][j]=(_ans.mat[i][j]+_a.mat[i][k]*_b.mat[k][j])%M::MODNUM;
    _ans.r=_a.r;_ans.c=_b.c;
    return _ans;
}

 

 

Examples

  P3390 【模板】 矩阵快速幂

  Description

    给定n*n的矩阵A,求A^k

  Input

    第一行,n,k

    第2至n+1行,每行n个数,第i+1行第j个数表示矩阵第i行第j列的元素

  Output

    输出A^k

    共n行,每行n个数,第i行第j个数表示矩阵第i行第j列的元素,每个元素模10^9+7

  Sample Input

2 1
1 1
1 1

 

  Sample Output

1 1
1 1

 

  Hint

    n<=100, k<=10^12, |矩阵元素|<=1000

  Soltion

    板子题要啥Solution

  Code

 

#include<cstdio>
#include<cstring>
#define rg register
#define ci const int
#define cl const long long int

typedef long long int ll;

namespace IO {
    char buf[100];
}

template <typename T>
inline void qr(T &x) {
    char ch=getchar(),lst=' ';
    while(ch>'9'||ch<'0') lst=ch,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(lst=='-') x=-x;
}

template <typename T>
inline void write(T x,const char aft,const bool pt) {
    if(x<0) {putchar('-');x=-x;}
    int top=0;
    do {
        IO::buf[++top]=x%10+'0';
        x/=10;
    }while(x);
    while(top) putchar(IO::buf[top--]);
    if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T _a,const T _b) {if(_b<_a) return _a;return _b;}
template <typename T>
inline T mmin(const T _a,const T _b) {if(_a>_b) return _b;return _a;}
template <typename T>
inline T mabs(const T _a) {if(_a<0) return -_a;return _a;}

template <typename T>
inline void mswap(T &_a,T &_b) {
    T _temp=_a;_a=_b;_b=_temp;
}

const int maxn = 110;
const int MOD = 1e9+7;

struct M {
    ll mat[maxn][maxn];
    void clear() {
        memset(mat,0,sizeof mat);
    }
};
M MU,re,ans;

int n;ll k;

void mul(const M &_a,const M & _b,M &_c) {
    M _temp;_temp.clear();
    for(rg int i=1;i<=n;++i) {
        for(rg int j=1;j<=n;++j) {
            for(rg int k=1;k<=n;++k)
                _temp.mat[i][j]=(_temp.mat[i][j]+_a.mat[i][k]*_b.mat[k][j]%MOD)%MOD;
        }
    }
    for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) _c.mat[i][j]=_temp.mat[i][j];
}

int main() {
    qr(n);qr(k);
    for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) {
        M &now=MU;qr(now.mat[i][j]);
    }
    for(rg int i=1;i<=n;++i) ans.mat[i][i]=1;
    
    while(k) {
        if(k&1) mul(ans,MU,ans);
        mul(MU,MU,MU);
        k>>=1;
    }
    
    for(rg int i=1;i<=n;++i) {
        for(rg int j=1;j<n;++j) write(ans.mat[i][j],' ',true);
        write(ans.mat[i][n],'\n',true);
    }
}

    【P1962】 斐波那契数列 

    Description

      求斐波那契数第n项。其中规定f0=0,f1=1

    Input

      一个数,n

    Output

      一个数,答案取模1e9+7。

    Sample Input

10

    Sample Output

55

    Hint

      n在long long范围内

    Solution

      因为知道数列中连续两项就可以推出后面所有的数,所以考虑把两个连续的数放到一个矩阵中。

      考虑|fi,fi+1|*X=|fi+1,fi+2|。记A=|fi,fi+1|,B=|fi+1,fi+2|。r代表行数,c代表列数。

      由矩阵乘法的性质可知,A.c=X.r;B.c=X.c。所以X是二行二列矩阵

      设

      根据矩阵的乘法法则,可以列出方程:

      a*fi+c*fi+1=fi+1

      b*fi+d*fi+1=fi+2

      直接利用①两侧相等解出a=0,c=1。

      对于方程②使用递推式解得b=d=1。

      这样矩阵x就被解出。

      最后答案即是|f1,f2|*Xn-1

    Code

#include<cstdio>
#include<cstring>
#define rg register
#define ci const int
#define cl const long long int

typedef long long int ll;

namespace IO {
    char buf[100];
}

template <typename T>
inline void qr(T &x) {
    char ch=getchar(),lst=' ';
    while(ch>'9'||ch<'0') lst=ch,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(lst=='-') x=-x;
}

template <typename T>
inline void write(T x,const char aft,const bool pt) {
    if(x<0) {putchar('-');x=-x;}
    int top=0;
    do {
        IO::buf[++top]=x%10+'0';
        x/=10;
    }while(x);
    while(top) putchar(IO::buf[top--]);
    if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T _a,const T _b) {if(_b<_a) return _a;return _b;}
template <typename T>
inline T mmin(const T _a,const T _b) {if(_a>_b) return _b;return _a;}
template <typename T>
inline T mabs(const T _a) {if(_a<0) return -_a;return _a;}

template <typename T>
inline void mswap(T &_a,T &_b) {
    T _temp=_a;_a=_b;_b=_temp;
}

const int MOD = 1000000007;

struct M {
    static const int Maxsize = 10;
    static const int MODNUM = 1000000007;
    int r,c;
    ll mat[Maxsize][Maxsize];
    void clear() {memset(mat,0,sizeof mat);r=c=0;}
    bool can_mul(const M &_a,const M _b) {
        return _a.c==_b.r;
    }
    bool can_add(const M &_a,const M &_b) {
        return (_a.r==_b.r) and (_a.c==_b.c);
    }
};
M operator+(const M &_a,const M &_b) {
    M _ans;_ans.clear();
    for(rg int i=1;i<=_a.r;++i) for(rg int j=1;j<=_a.c;++j) _ans.mat[i][j]=(_a.mat[i][j]+_b.mat[i][j])%M::MODNUM;
    _ans.r=_a.r;_ans.c=_a.c;
    return _ans;
}
M operator*(const M &_a,const M &_b) {
    M _ans;_ans.clear();
    for(rg int i=1;i<=_a.r;++i) 
        for(rg int j=1;j<=_b.c;++j)
            for(rg int k=1;k<=_a.c;++k)
                _ans.mat[i][j]=(_ans.mat[i][j]+_a.mat[i][k]*_b.mat[k][j])%M::MODNUM;
    _ans.r=_a.r;_ans.c=_b.c;
    return _ans;
}
M fib,beg,MS;

ll n;

void beginning();
M pow(ll);

int main() {
    beginning();
    qr(n);
    if(n<3ll) return printf("1\n")&0;
    M _ans=pow(n-1); fib=beg*_ans;
    write(fib.mat[1][1],'\n',true);
    return 0;
}

void beginning() {
    beg.r=1;beg.c=2;
    beg.mat[1][2]=beg.mat[1][1]=1;
    MS.r=MS.c=2;
    MS.mat[1][2]=MS.mat[2][1]=MS.mat[2][2]=1;
}

M pow(ll x) {
    M _ans;_ans.clear();_ans.r=2;_ans.c=2;
    _ans.mat[1][1]=_ans.mat[2][2]=1;
    while(x) {
        if(x&1) _ans=_ans*MS;
        MS=MS*MS;
        x>>=1;
    }
    return _ans;
}

Summary

  这是矩阵中比较基础的部分,事实上,对于循环形式的快速幂,还是要好好掌握滴。

  行列式真tm变态不会不会滚粗了

posted @ 2018-08-16 18:15  一扶苏一  阅读(761)  评论(0编辑  收藏  举报