矩阵相关

矩阵前置芝士

矩阵:看成一个二维数组就好了.

矩阵加法:

本人认为没用
条件:两个大小相同的矩阵
对应位置相加,形成相同大小的矩阵.

矩阵减法:

本人认为没用
条件:两个大小相同的矩阵

对应位置相减,形成相同大小的矩阵.

矩阵乘法:

这个很重要
条件:两个大小不完全一样的矩阵,必须保证矩阵\(A(n*k)\),矩阵B大小是\(k*m\)的,形成的矩阵C大小是\(n*m\)
C矩阵中的x,y位置是A矩阵的X行,B矩阵的Y列相加.
代码:

for(int i = 1;i <= n;++ i) {
    for(int j = 1;j <= m;++ j) {
        for(int l = 1;l <= k;++ l) 
            C[i][j] += A[i][k] + B[k][j];
    }
}

性质:
矩阵乘法具有结合律:
\(A*B*C = (A*B)*C = A*(B*C)\)

矩阵快速幂

ps:使用矩阵快速幂的时候一定要有模数.
根据矩阵乘法具有结合律的性质,我们可以做矩阵的快速幂:
例题---Luogu矩阵快速幂
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
const int mod = 1e9 + 7;
const int X = 110;

ll n,k;

struct Node {
    ll a[X][X];
    void clear() {
        memset(a,0,sizeof(a));
    }
}res,ans;

Node mul(Node a,Node b) {
    Node tmp;
    tmp.clear();
    for(int i = 1;i <= n;++ i) {
        for(int j = 1;j <= n;++ j) {
            for(int k = 1;k <= n;++ k) {
                tmp.a[i][j] = (tmp.a[i][j] + (a.a[i][k] * b.a[k][j]) % mod ) % mod;
            } 
        } 
    }
    return tmp;
}

void fast_pow(ll k) {
    for(int i = 1;i <= n;++ i)
        ans.a[i][i] = 1;
    for(;k;k >>= 1,res = mul(res,res)) {
        if(k & 1)ans = mul(ans,res);
    }
    return ;
}

int main() {
    scanf("%lld%lld",&n,&k);
    for(int i = 1;i <= n; ++i ) {
        for(int j = 1;j <= n;++ j) {
            scanf("%lld",&res.a[i][j]);
        }
    }
    ans.clear();
    fast_pow(k);
    for(int i = 1;i <= n;++ i) {
        for(int j = 1;j <= n;++ j) {
            printf("%lld ",ans.a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

/*
5
5
1 1 1 1 1
1 0 0 1 0
0 0 2 3 1
0 0 0 1 0
5 5 5 5 5
*/

斐波那契数列

我们可以通过矩阵快速幂来优化斐波那契数列斐波那契数列:$$f[n] = f[n - 1] + f[n - 2]$$
放道例题:
斐波那契数列---Luogu1962
如何做:
斐波那契数列的通项公式???
显然不行,有精度问题.
那我们来做矩阵快速幂
题目中有f[1] = 1,f[2] = 1
先构造矩阵(先不用管为什么这样构造):

\[ \left\{ \begin{matrix} 1 & 1 \\ \end{matrix} \right\} \tag{1} \]

表示

\[ \left\{ \begin{matrix} 斐波那契数列第二项 & 斐波那契数列第一项 \\ \end{matrix} \right\} \tag{2} \]

尝试往后推一项
我们想要矩阵成这个样子

\[ \left\{ \begin{matrix} 斐波那契数列第三项 & 斐波那契数列第二项 \\ \end{matrix} \right\} \tag{2} \]

那我们要乘一个矩阵
首先根据矩阵的定义,我们必须要构造一个\(2*2\)的矩阵
首先给出构造好的矩阵:

\[ \left\{ \begin{matrix} 1 & 1\\ 1 & 0 \\ \end{matrix} \right\} \tag{3} \]

模拟一下就好了.
构造就是 根据生成的矩阵来构造中间的这个矩阵
我们再回过头来看一下,为什么刚开始的矩阵是

\[ \left\{ \begin{matrix} 1 & 1 \\ \end{matrix} \right\} \tag{1} \]

因为f[3]要用到这两项啊.
记住,矩阵快速幂中间的矩阵不可改变.
这样我们就可以进行中间的矩阵\(n -2\)次幂的运算来获得一个矩阵啦,
然后用刚开始的矩阵与这个矩阵相乘就得到第n项了.
注意特判\(n == 1,n == 2\)的特殊情况

#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
const int maxN = 100 + 7;
const ll mod = 1000000007;
using namespace std;

ll n,m,k;
struct Marix{
    ll s[maxN][maxN];
    void clear() {
        memset(s,0,sizeof(s));
        return;
    }
}ans,now,a;

Marix work(Marix a,Marix b) {
    Marix c;
    c.clear();
    for(ll i = 1;i <= n;++ i) 
        for(ll j = 1;j <= n;++ j) 
            for(ll k = 1;k <= n;++ k) 
                c.s[i][j] = ( c.s[i][j]  % mod+ ( a.s[i][k] * b.s[k][j] ) % mod ) % mod;
    return c;
}

void fast_pow() {
    ans.clear();
    for(ll i = 1;i <= n;++ i) {
        ans.s[i][i] = 1;
    }
    for(now = a;k;k >>= 1,now = work(now,now)) {
        if(k & 1) ans = work(ans,now);
    }
    Marix Q;
    Q.s[1][1] = 1;Q.s[1][2] = 1;
    ans = work(Q,ans);
    printf("%lld",ans.s[1][1]);
}

int main() {
    n = 2;
    scanf("%lld",&k);
    if(k == 1) {
    	printf("1");
    	return 0;
    }
    if(k == 2) {
        printf("1");
        return 0;
    }
    k -= 2;
    a.s[1][1] = 1;a.s[1][2] = 1;
    a.s[2][1] = 1;a.s[2][2] = 0;
    fast_pow();
    return 0;
}

矩阵递推相关

给道例题:Luogu1939
建议自己先构造一下:
开始矩阵:

\[ \left\{ \begin{matrix} 1 (第一项)& 1(第二项) & 1(第三项)\\ \end{matrix} \right\} \tag{1} \]

中间的矩阵:

\[ \left\{ \begin{matrix} 0 & 0 & 1\\ 1 & 0 &0 \\ 0 & 1 & 1\\ \end{matrix} \right\} \tag{1} \]

#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
const int maxN = 100 + 7;
const int mod = 1e9 + 7;
using namespace std;

ll n,m,k;
struct Marix{
    ll s[maxN][maxN];
    void clear() {
        memset(s,0,sizeof(s));
        return;
    }
}ans,now,a;

Marix work(Marix a,Marix b) {
    Marix c;
    c.clear();
    for(ll i = 1;i <= n;++ i) 
        for(ll j = 1;j <= n;++ j) 
            for(ll k = 1;k <= n;++ k) 
                c.s[i][j] = ( c.s[i][j]  % mod+ ( a.s[i][k] * b.s[k][j] ) % mod ) % mod;
    return c;
}

void fast_pow() {
    ans.clear();
    for(ll i = 1;i <= n;++ i) {
        ans.s[i][i] = 1;
    }
    for(now = a;k;k >>= 1,now = work(now,now)) {
        if(k & 1) ans = work(ans,now);
    }
    Marix Q;
    Q.s[1][1] = 1;Q.s[1][2] = 1;Q.s[1][3] = 1;
    ans = work(Q,ans);
    printf("%lld\n",ans.s[1][3]);
}

int main() {
    n = 3;
    int T;
    scanf("%d",&T);
    while(T --) {
        a.s[1][3] = a.s[2][1] = a.s[3][2] = a.s[3][3] = 1;
        scanf("%lld",&k);
        k -= 3;
        if(k <= 0) printf("1\n");
        else fast_pow();
    }
    return 0;
}

矩阵快速幂优化DP

设置状态f[i][j][k]表示i到j走k步的方案数
设f[i][j][1] = 0/1表示i到j有没有路(0代表无路,1代表有)
枚举中间点Q
\(f[i][j][k] = \sum_{Q=1}^{n}f[i][Q][k - 1] * f[Q][j][1]\)
HDU2157

#include <iostream>
#include <cstdio>
#include <cstring>
const int maxN = 20 + 7;
const int mod = 1000;
using namespace std;

struct Matrix
{
	int s[maxN][maxN];
	Matrix() {memset(s,0,sizeof(s));}
};

int n,m;

Matrix operator * (const Matrix &a,const Matrix &b) {
	Matrix c;
	for(int i = 1;i <= n;++ i) {
		for(int j = 1;j <= n;++ j) {
			for(int k = 1;k <= n;++ k) {
				c.s[i][j] = (c.s[i][j] + a.s[i][k] * b.s[k][j] % mod) % mod;
			}
		}
	}
	return c;
}

Matrix fast_pow(Matrix a,int nn) {
	Matrix ans;
	for (int i = 1;i <= n; ++i)
		ans.s[i][i] = 1;
	for (; nn; nn >>= 1,a = a * a)
	{
		if(nn & 1) ans = ans * a;
	}
	return ans;
}

int main()
{
	while(~scanf("%d%d",&n,&m) && (n || m)) {
		Matrix q;
		for(int i = 1,u,v;i <= m;++ i) {
			scanf("%d%d",&u,&v);
			q.s[u + 1][v + 1] = 1;
		}	
		int T;
		scanf("%d",&T);
		int s,t,k;
		while(T --) {
			scanf("%d%d%d",&s,&t,&k);
			Matrix b;
			b = fast_pow(q,k);
			printf("%d\n", b.s[s + 1][t + 1]);
		}
	}
	return 0;
}
posted @ 2018-08-25 19:00  Rlif  阅读(475)  评论(0编辑  收藏  举报