乘法矩阵几个常用法和注意点

阅读前请先掌握矩阵乘法的运算规则(自行百度

从易到难,从题目中学习

矩阵快速幂(模板)

先掌握矩阵乘法,以及快速幂的写法

乘法,三层for循环,按照矩阵乘法规则写就行,快速幂就是普通快速幂板子

注意点:快速幂里,ans需要先定义为单位矩阵,结构体里二维数组需要初始化

矩阵左乘,不具有交换律 base * ans  !=  ans * base 

 

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define _for(i,s,b) for(int i=(s) ;i<=(b) ;i++)
#define _rep(i,s,b) for(int i=(s) ;i>=(b) ;i--)
#define mst(x,y) memset(x,y,sizeof(x))
#define all(v) v.begin() , v.end()
#define pb(v) push_back(v)
#define INF 0x3f3f3f3f
#define int long long
#define lson p*2,l,mid
#define rson p*2+1,mid+1,r
typedef long long ll;
int n,k;

const int N =105;
const int md = 1e9+7;
struct matrix
{
    ll a[N][N]={0};
    matrix operator * ( matrix &b)//重定义矩阵乘法
    {
        matrix d;
        _for(i,1,n)
        {
            _for(j,1,n)
            {
                _for(k,1,n)
                {
                  d.a[i][j] = (d.a[i][j] + a[i][k] * b.a[k][j]%md ) %md;
                }
            }
        }
        return d;
    }
};
matrix qsm(matrix a,int k)//矩阵快速幂
{
    matrix ans, temp=a;
    mst(ans.a,0);
    _for(i,1,n) ans.a[i][i] = 1;//建造单位矩阵
    while( k )
    {
        if( k & 1) ans = (ans * temp ) ;
        temp = ( temp * temp );
        k>>=1;
    }
    return ans;
}
signed main()
{
    matrix x,y,z;
    cin>>n>>k;//输入n*n矩阵,k次幂
    _for(i,1,n)
    _for(j,1,n) cin>>x.a[i][j];

    x= qsm(x,k);
    _for(i,1,n)
    {
        _for(j,1,n)
        cout<<x.a[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

矩阵加速

一般用于递推式里,不需要一项一项推,只要知道函数关系式,推出矩阵然后矩阵快速幂一下就行,O(N)变O(logn);

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define _for(i,s,b) for(int i=(s) ;i<=(b) ;i++)
#define _rep(i,s,b) for(int i=(s) ;i>=(b) ;i--)
#define mst(x,y) memset(x,y,sizeof(x))
#define all(v) v.begin() , v.end()
#define pb(v) push_back(v)
#define INF 0x3f3f3f3f
#define int long long
#define lson p*2,l,mid
#define rson p*2+1,mid+1,r
typedef long long ll;
int n=3;

const int N =105;
const int md = 1e9+7;
struct matrix
{
    ll a[N][N]={0};
    matrix operator * ( matrix &b)//重定义矩阵乘法
    {
        matrix d;
        mst(d.a,0);//记得初始化
        _for(i,1,n)
        {
            _for(j,1,n)
            {
                _for(k,1,n)
                {
                  d.a[i][j] = (d.a[i][j] + a[i][k] * b.a[k][j]%md ) %md;
                }
            }
        }
        return d;
    }
}ans,base;
matrix qsm(matrix a,int k)//矩阵快速幂
{
    matrix ans, temp=a;
    mst(ans.a,0);
    _for(i,1,n) ans.a[i][i] = 1;//建造单位矩阵
    while( k )
    {
        if( k & 1) ans = (ans * temp ) ;
        temp = ( temp * temp );
        k>>=1;
    }
    return ans;
}
signed main()
{
    IOS;
    int T;
    cin>>T;

    while( T-- )
    {
        mst(base.a,0);
        base.a[1][1]=1;
        base.a[1][3]=1;
        base.a[2][1]=1;
        base.a[3][2]=1;
        mst(ans.a,0);
        _for(i,1,3) ans.a[i][1]=1;

        int x;
        cin>>x;
        if( x<=3 )  {cout<<1<<endl;continue;}
        base = qsm(base,x-3);
        ans = base * ans ;//注意矩阵左乘,不具有交换律

        cout<<ans.a[1][1]<<endl;
    }
}

斐波那契数列

会了矩阵加速,来个入门实战,用这个方法推肥波就很舒服了。

相比于直接递推,矩阵加速节省了内存,减少了时间,可谓利器,用递推的话,这题铁re


#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(x,y) memset(x,y,sizeof(x))
#define all(v) v.begin() , v.end()
#define pb(v) push_back(v)
#define INF 0x3f3f3f3f
#define int long long
#define lson p*2,l,mid
#define rson p*2+1,mid+1,r
typedef long long ll;
const int N = 3;
const int mod = 1e9+7;
int n;
struct mat
{
    ll a[N][N]={0};
    mat operator * (const mat & b)
    {
        mat res;
        _for(i,1,2)
        {
            _for(j,1,2)
            {
                _for(k,1,2)
                {
                    res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]%mod)%mod;
                }
            }
        }
        return res;
    }
}ans,base;
mat qsm(mat a, ll k)
{
    mat ans , temp = a;
    mst(ans.a,0);
    _for(i,1,3) ans.a[i][i] =1;
    while( k )
    {
        if( k & 1) ans = ans * temp ;
        temp = temp * temp ;
        k>>=1;
    }
    return ans;
}
signed main()
{
    //!!!!!!!!!!!!!!!!!!!!!!
//    freopen("data.txt","r",stdin);
    //!!!!!!!!!!!!!!!!!!!!!!
    IOS;
    mst(base.a,0);
    mst(ans.a,0);
    base.a[1][1]=1;
    base.a[1][2]=1;
    base.a[2][1]=1;
    ans.a[1][1]=1;
    ans.a[2][1]=1;
    cin>>n;
    if( n<=2 )
    {
        cout<<1<<endl;
        return 0;
    }
    base = qsm(base , n-1);
    ans = ans * base;
    cout<<ans.a[1][1]<<endl;
}

群鸡乱舞

思路:dp,矩阵乘优化(为了学个矩阵乘溯源两天

dp[i][j]表示第i年年龄为j的小鸡的数量

转移方程

j=0   dp[i][0] = \sum_{k=1}^{t-2} dp[i-1][k] 今年出生的小鸡是去年1 ~ t-2 的小鸡数量

j!=0  dp[i][j] = dp[i-1][j-1]今年j岁的小鸡数量等于去年j-1岁的小鸡数量

做到这里只能过45%,后面会tle,考虑矩阵加速一下

推一下矩阵式,设t=4,也就是小鸡年龄在[0,3]间,设初始小鸡的年龄矩阵为(这里设为1列矩阵)

\begin{bmatrix} x\\ y\\ z\\ a\\ \end{bmatrix} x,y,z,a分别对应年龄为0,1,2,3的小鸡数量,推下一年的年龄矩阵为

\begin{bmatrix} sum\\ x\\ y\\ z\\ \end{bmatrix}sum = x + y +  z 

从而推得递推矩阵

\begin{pmatrix} &0 &1 &1 &0 \\ &1 &0 &0 &0 \\ &0 &1 &0 &0 \\ &0 &0 &1 &0 \\ \end{pmatrix}

 

//dp,矩阵乘
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(x,y) memset(x,y,sizeof(x))
#define all(v) v.begin() , v.end()
#define pb(v) push_back(v)
#define INF 0x3f3f3f3f
#define int long long
#define lson p*2,l,mid
#define rson p*2+1,mid+1,r
#define int long long
typedef long long ll;
const int N = 75;
int mod;
int n,m,t;
struct mat
{
    ll a[N][N]={0};
    mat operator * (const mat & b)
    {
        mat res;
        _for(i,0,t)
        {
            _for(j,0,t)
            {
                _for(k,0,t)
                {
                    res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]%mod)%mod;
                }
            }
        }
        return res;
    }
}ans,base;
mat qsm(mat a, ll k)
{
    mat ans , temp = a;
    mst(ans.a,0);
    _for(i,0,t-1) ans.a[i][i] =1;
    while( k )
    {
        if( k & 1) ans = ans * temp ;
        temp = temp * temp ;
        k>>=1;
    }
    return ans;
}
ll gcd(ll a, ll b)
{
    return b==0?a:gcd(b,a%b);
}
signed main()
{
    //!!!!!!!!!!!!!!!!!!!!!!
//    freopen("data.txt","r",stdin);
    //!!!!!!!!!!!!!!!!!!!!!!
    IOS;
    cin>>n;
    _for(i,1,n)
    {
        int x;
        cin>>x;
        ans.a[x][0]++;
    }
    cin>>t>>m>>mod;
    //ini
    _for(i,1,t-1)
    {
        base.a[i][i-1]=1;
    }
    _for(i,1,t-2)
    {
        base.a[0][i] = 1;
    }
    base = qsm( base , m-1);
    ans = base * ans;
    ll res = 0;
    _for(i,0,t-1)
    {
        res = (res + ans.a[i][0])%mod;
    }
    cout<<res<<endl;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值