矩阵乘法题集

矩阵 A×B

模板题。

感觉写完结构体就可以为所欲为了。

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 100 + 10;
struct Matrix
{
	int n, m;
	ll c[MAXN][MAXN];
	
	Matrix() { memset(c, 0, sizeof(c)); }
	
	void read()
	{
		_for(i, 1, n)
			_for(j, 1, m)
				scanf("%lld", &c[i][j]);
	}
	
	Matrix operator * (const Matrix& a)
	{
		Matrix r;
		r.n = n; r.m = a.m;
		_for(i, 1, r.n)
			_for(j, 1, r.m)
				_for(k, 1, m)
					r.c[i][j] += c[i][k] * a.c[k][j];
		return r;
	}
	
	void print()
	{
		_for(i, 1, n)
		{
			_for(j, 1, m)
				printf("%lld ", c[i][j]);
			puts("");
		}
	}
}A, B, C;

int main()
{
	scanf("%d%d", &A.n, &A.m); B.n = A.m;
	A.read();
	scanf("%d", &B.m);
	B.read();
	C = A * B;
	C.print();
	return 0;
}

Fibonacci 第 n 项

大概总结一下构造矩阵的方法

fn = fn-1 + fn-2

我们可以构造一个1x2的矩阵

使得

[fn-2, fn-1] * A = [fn-1, fn] = [fn-1, fn-1+fn-2]

A是一个2*2的矩阵

可以求得A是

0 1
1 1

矩阵快速幂即可

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 100 + 10;
int MOD, k;

struct Matrix
{
	int n, m;
	ll c[MAXN][MAXN];
	
	Matrix() { memset(c, 0, sizeof(c)); }
	
	Matrix operator * (const Matrix& a)
	{
		Matrix r;
		r.n = n; r.m = a.m;
		_for(i, 1, r.n)
			_for(j, 1, r.m)
				_for(k, 1, m)
					r.c[i][j] = (r.c[i][j] + c[i][k] * a.c[k][j] % MOD) % MOD;
		return r;
	}
};

Matrix power(Matrix a, int b)
{
	Matrix res; res.n = res.m = 2;
	_for(i, 1, res.n) res.c[i][i] = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a;
		a = a * a;
	}
	return res;
}

int main()
{
	scanf("%d%d", &k, &MOD);
	Matrix A; A.n = A.m = 2;
	A.c[1][2] = A.c[2][1] = A.c[2][2] = 1;
	A = power(A, k - 1);
	
	Matrix ans; ans.n = 1; ans.m = 2;
	ans.c[1][1] = ans.c[1][2] = 1;
	ans = ans * A;
	printf("%lld
", ans.c[1][1]);
	
	return 0;
}

Fibonacci 前 n 项和

在矩阵中加入一个sn进去就好了。

构造矩阵记得往后推以及分解

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 100 + 10;
int MOD, k;

struct Matrix
{
	int n, m;
	ll c[MAXN][MAXN];
	
	Matrix() { memset(c, 0, sizeof(c)); }
	
	Matrix operator * (const Matrix& a)
	{
		Matrix r;
		r.n = n; r.m = a.m;
		_for(i, 1, r.n)
			_for(j, 1, r.m)
				_for(k, 1, m)
					r.c[i][j] = (r.c[i][j] + c[i][k] * a.c[k][j] % MOD) % MOD;
		return r;
	}
};

Matrix power(Matrix a, int b)
{
	Matrix res; res.n = res.m = 3;
	_for(i, 1, res.n) res.c[i][i] = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a;
		a = a * a;
	}
	return res;
}

int main()
{
	scanf("%d%d", &k, &MOD);
	Matrix A; A.n = A.m = 3;
	A.c[1][1] = A.c[2][1] = A.c[3][1] = 1;
	A.c[3][2] = A.c[3][3] = A.c[2][3] = 1;
	A = power(A, k - 2);
	
	Matrix ans; ans.n = 1; ans.m = 3;
	ans.c[1][1] = 2;
	ans.c[1][2] = ans.c[1][3] = 1;
	ans = ans * A;
	printf("%lld
", ans.c[1][1]);
	
	return 0;
}

佳佳的 Fibonacci

这道题推的真的太爽了!!

看到有n*fn

那么我们就再矩阵中加入n*fn

所以就是

[Tn-1, (n-2)Fn-2, (n-1)Fn-1,Fn-1,Fn-2]

推到

[Tn, (n-1)Fn-1, nFn,Fn,Fn-1]

所以可以构造出A

1 0 0 0 0

1 0 1 0 0

1 1 1 0 0

1 0 1 1 1

2 0 2 1 0

初始矩阵为[3,1,2,1,1]

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 100 + 10;
int MOD, k;

struct Matrix
{
	int n, m;
	ll c[MAXN][MAXN];
	
	Matrix() { memset(c, 0, sizeof(c)); }
	
	Matrix operator * (const Matrix& a)
	{
		Matrix r;
		r.n = n; r.m = a.m;
		_for(i, 1, r.n)
			_for(j, 1, r.m)
				_for(k, 1, m)
					r.c[i][j] = (r.c[i][j] + c[i][k] * a.c[k][j] % MOD) % MOD;
		return r;
	}
};

Matrix power(Matrix a, int b)
{
	Matrix res; res.n = res.m = 5;
	_for(i, 1, res.n) res.c[i][i] = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a;
		a = a * a;
	}
	return res;
}

int main()
{
	scanf("%d%d", &k, &MOD);
	if(k == 1) { puts("1"); return 0; }
	Matrix A; A.n = A.m = 5;
	A.c[1][1] = 1;
	A.c[2][1] = A.c[2][3] = 1;
	A.c[3][1] = A.c[3][2] = A.c[3][3] = 1;
	A.c[4][1] = A.c[4][3] = A.c[4][4] = A.c[4][5] = 1;
	A.c[5][1] = A.c[5][3] = 2; A.c[5][4] = 1;
	A = power(A, k - 2);
	
	Matrix ans; ans.n = 1; ans.m = 5;
	ans.c[1][1] = 3;
	ans.c[1][2] = ans.c[1][4] = ans.c[1][5] = 1;
	ans.c[1][3] = 2;
	ans = ans * A;
	printf("%lld
", ans.c[1][1]);
	
	return 0;
}

bzoj 1009

这道题写了好久!!!

这道题用到了三个知识点,kmp,dp,矩阵乘法。

设f[i][j]为前i个字符的后缀匹配到不吉利串的前j个字符的方案数(第一次见到这样的状态设计)

那么转移方程为f[i][next(j)] = sum(f[i-1][j])

从j到next(j)可以用kmp算法求出来

然后这个转移可以用矩阵快速幂来优化。

注意这里的矩阵和状态是相匹配的,所以下标一定从0开始

所以矩阵乘法真正的用途在与dp,如果题目给的n很大,

那么可以按照转移的方式来构造矩阵

#include<bits/stdc++.h>
#define add(a, b) a = (a + b) % mod
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 30;
int next[MAXN], mod, m, n;
char s[MAXN];

struct Matrix
{
	int n, m;
	int c[MAXN][MAXN];
	
	Matrix(int n, int m) : n(n), m(m) { memset(c, 0, sizeof(c)); }
	
	Matrix operator * (const Matrix& a)
	{
		Matrix res(n, a.m);
		REP(i, 0, res.n)
			REP(j, 0, res.m)
				REP(k, 0, m)
				{
					res.c[i][j] += c[i][k] * a.c[k][j];
					res.c[i][j] %= mod;
				}
		return res;
	}
};

void get_next()
{
	next[1] = 0; int j = 0;
	_for(i, 2, m)
	{
		while(j && s[j + 1] != s[i]) j = next[j];
		if(s[j + 1] == s[i]) j++;
		next[i] = j;
	}
}

Matrix power(Matrix a, int b)
{
	Matrix res(m, m);
	REP(i, 0, m) res.c[i][i] = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a;
		a = a * a;
	}
	return res;
}

int main()
{
	scanf("%d%d%d%s", &n, &m, &mod, s + 1);
	get_next();
	
	Matrix A(m, m);
	REP(i, 0, m)
		REP(d, 0, 10)
		{
			int j = i; 
			char ch = d + '0';
			while(j && s[j + 1] != ch) j = next[j];
			if(s[j + 1] == ch) j++;
			if(j != m)add(A.c[i][j], 1);
		}
	
	Matrix ans(1, m);
	ans.c[0][0] = 1; 
	ans = ans * power(A, n);
	
	int sum = 0;
	REP(i, 0, m)
		add(sum, ans.c[0][i]);
	printf("%d
", sum);

	return 0;
}

[SCOI2009]迷路

首先这道题如果边权值为1的话,矩阵自乘T次就是答案

但是这道题的边权最大为9

所以拆成9个点。

可以定义一个id为id(x, y) ((x - 1) * 9 + y),也就是说给每个点拆出来的点给个

编号,然后连边的时候连编号,y为1就最原始的点,y!=1都是衍生出来的。

注意连边的时候注意方向,一定要分清x坐标和y坐标,可以拿笔画一画

#include<bits/stdc++.h>
#define id(x, y) ((x - 1) * 9 + y)
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
 
const int MAXN = 100 + 10;
const int mod = 2009;
int n, T;
struct Matrix
{
	int n, m;
	int c[MAXN][MAXN];
	
	Matrix(int n, int m) : n(n), m(m) { memset(c, 0, sizeof(c)); }
	
	Matrix operator * (const Matrix& a)
	{
		Matrix res(n, a.m);
		_for(i, 1, res.n)
			_for(j, 1, res.m)
				_for(k, 1, m)
				{
					res.c[i][j] += c[i][k] * a.c[k][j];
					res.c[i][j] %= mod;
				}
		return res;
	}
};

Matrix power(Matrix a, int b)
{
	Matrix res(n * 9, n * 9);
	_for(i, 1, n * 9) res.c[i][i] = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a;
		a = a * a;
	}
	return res;
}
 
int main()
{
	scanf("%d%d", &n, &T);
	Matrix A(n * 9, n * 9);
	
	_for(i, 1, n)
		_for(j, 1, n)
		{
			int x; scanf("%1d", &x);
			if(!x) continue;
			A.c[id(i, x)][id(j, 1)] = 1;
		}
	
	_for(i, 1, n)
		_for(j, 1, 8)
			A.c[id(i, j)][id(i, j + 1)] = 1;
	
	A = power(A, T);
	printf("%d
", A.c[id(1, 1)][id(n, 1)]);
	
	return 0;
}

bzoj 1898

我又独立做出省选题了,这次只WA了一次

但是这次WA显然可以避免的,自己要养成写完程序静态查错的习惯,不要直接交,然后再改。

考试的时候只能交一次。还有头脑一定要非常清晰自己在干什么。

这道题关键在于用到了lcm的思想。

美人鱼只有2,3,4,那么显然12秒之后又会重复。

那么我们可以针对每一秒构造出转移矩阵,注意是每一秒,不是前几秒,我一开始写的是前几秒。

每一秒就用dp的方式构造,这个dp非常好写。

然后注意状态矩阵和转移矩阵要分清

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
  
const int MAXN = 50 + 10;
const int mod = 10000;
int n, m, st, en, K, nfish;
int g[MAXN][MAXN];
vector<int> fish[MAXN];
 
struct Matrix
{
    int n, m;
    int c[MAXN][MAXN];
     
    Matrix() { memset(c, 0, sizeof(c)); }
    void init(int p) { n = m = p; }
     
    Matrix operator * (const Matrix& a)
    {
        Matrix res;
        res.n = n; res.m = a.m;
        REP(i, 0, res.n)
            REP(j, 0, res.m)
                REP(k, 0, m)
                {
                    res.c[i][j] += c[i][k] * a.c[k][j];
                    res.c[i][j] %= mod;
                }
        return res;
    }
}A[15], ans;
 
Matrix power(Matrix a, int b)
{
    Matrix res; res.init(n);
    REP(i, 0, n) res.c[i][i] = 1;
    for(; b; b >>= 1)
    {
        if(b & 1) res = res * a;
        a = a * a;
    }
    return res;
}
 
void read()
{
    scanf("%d%d%d%d%d", &n, &m, &st, &en, &K);
    _for(i, 1, m)
    {
        int x, y; scanf("%d%d", &x, &y);
        g[x][y] = g[y][x] = 1;
    }
    scanf("%d", &nfish);
    REP(i, 0, nfish)
    {
        int x, y; scanf("%d", &x);
        while(x--)
        {
            scanf("%d", &y);
            fish[i].push_back(y);
        }
    }
}
 
void get_Matrix()
{
    _for(i, 0, 12) A[i].init(n);
     
    _for(t, 1, min(12, K))
    {
        int vis[MAXN] = {0};
        REP(i, 0, nfish)
            vis[fish[i][t % fish[i].size()]] = 1;
             
        REP(i, 0, n) if(!vis[i])
            REP(j, 0, n) if(g[i][j])
                    A[t].c[j][i]++;
    }
}
 
void work()
{
    ans.n = 1; ans.m = n;
    ans.c[0][st] = 1;
	
    Matrix t; t.init(n);
	REP(i, 0, n) t.c[i][i] = 1; 
    if(K >= 12)
    {
        _for(i, 1, 12)  t = t * A[i];
        t = power(t, K / 12);
    }
    _for(i, 1, K % 12) t = t * A[i];
 
 	ans = ans * t;
    printf("%d
", ans.c[0][en]);
}
  
int main()
{
    read();
    get_Matrix();
    work();
    return 0;
}
原文地址:https://www.cnblogs.com/sugewud/p/9819298.html