0%

矩阵迭代加速

一、矩阵快速幂

矩阵快速幂和普通快速幂一样,只需要创建矩阵类型,对乘法进行重载

二、矩阵加速

差分方程问题

  1. 斐波那契数列:

    快速求第 $ n $ 项,可以使用通项公式,也可以使用矩阵快速幂

    因此可以得到

    基本上这个系数矩阵的第一行表示的是差分关系,然后其他的都是每行只有一个 $ 1 $ ,是一个 $ I $ 矩阵

  2. 类斐波那契数列:

三、例题

1. P3216 [HNOI2011]数学作业

发现数据范围非常大, $ O(n) $ 根本解决不了,需要 $ \log(n) $ 的做法。

$ f_n = f_{n-1} \times 10^{k} + n $ ,因此很容易可以得到这个矩阵乘法,然后使用加速幂

但是发现,指数 k 的值为 $ \left\lfloor\log_{10}i\right\rfloor+1 $ (也就是 $ len(i) $ 的值是会变化的,没法直接矩乘。由于 $ len(i) $ 的值一共只有 $ 18 $ 种左右,所以我们直接枚举 然后分 $ len(i) $ 块矩乘后合并。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <bits/stdc++.h>
#define ll long long
namespace FAST_IO
{
template <class T>
inline void read(T &x)
{
T flag = 1;
x = 0;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
flag = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
}
x *= flag;
}

template <class T, class... _T>
inline void read(T &x, _T &...y)
{
return read(x), read(y...);
}

} // namespace FAST_IO
using namespace std;
using namespace FAST_IO;
ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;
const double eqs = 1e-5;
const int maxn = 10 + 10;
const int maxm = 1e5 + 10;
ll t, n, m, k;

// 使用的时候注意一下矩阵乘法自带mod,矩阵的len,type等
struct Matrix
{
using type = ll;
static const int len = maxn;
type a[len][len];
int m, n; // 矩阵的行列
int dim; // 矩阵的维度
Matrix(int n, int m)
{
this->m = m, this->n = n;
clean();
}
Matrix(int n)
{
this->m = this->n = n;
clean();
}
Matrix(int n, char ch)
{
this->m = this->n = n;
if (ch == 'i' || ch == 'I')
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = (i == j);
}
}

void clean()
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = 0;
}

Matrix operator=(const Matrix &a)
{

for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
this->a[i][j] = a.a[i][j];
}
}
return *this;
}

Matrix operator%(const ll &mod) const
{
Matrix ret(n, m);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
ret.a[i][j] = a[i][j] % mod;
}
}
return ret;
}

Matrix operator%=(ll mod)
{
*this = *this % mod;
return *this;
}

Matrix operator*(const Matrix &a) const
{
Matrix ret(n, a.m);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= a.m; j++)
{
for (int k = 1; k <= m; k++)
{
ret.a[i][j] = (ret.a[i][j] + this->a[i][k] * a.a[k][j] % mod) % mod;
}
}
}
return ret;
}
Matrix operator*=(const Matrix &a)
{
*this = (*this) * a;
return *this;
}

type *operator[](const int x)
{
return a[x];
}

Matrix qpow(ll k)
{
Matrix ans(n, 'i'), tmp = *this;
while (k)
{
if (k & 1)
ans = ans * tmp;
tmp = tmp * tmp;
k >>= 1;
}
return ans;
}
};

int lenof(ll n)
{
int i;
for (i = 0; n; i++)
n /= 10;
return i;
}
ll pow10(int x)
{
ll ans = 1;
for (int i = 1; i <= x; i++)
ans *= 10;
return ans;
}
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
read(n, m);
Matrix a(3, 1);
mod = m;
int len = lenof(n);
a[1][1] = a[2][1] = a[3][1] = 1;
for (int i = 0; i < len - 1; i++)
{
Matrix b(3, 3);
b[1][1] = pow10(i + 1) % mod;
b[1][2] = b[1][3] = b[2][2] = b[2][3] = b[3][3] = 1;
m = pow10(i + 1) - pow10(i);
a = (b.qpow(m - (i == 0 ? 1 : 0))) * a;
}
Matrix b(3, 3);
b[1][1] = pow10(len) % mod;
b[1][2] = b[1][3] = b[2][2] = b[2][3] = b[3][3] = 1;
m = n - pow10(len - 1) + 1;
a = (b.qpow(m - (len - 1 == 0 ? 1 : 0))) * a;
printf("%lld\n", a[1][1]);
return 0;
}

2. P2886 [USACO07NOV]Cow Relays G

image-20210212184809306

还是使用矩阵乘法,矩阵乘法重载一下乘号,变成 $ \min $ 这种形式, $ a^k $ 表示恰好经过 $ k $ 条边最短路。

注意求的是最短路,然后需要初始化为 $ \inf $ ,注意这个里面 $ ans $ ,需要把对角线置为 $ 0 $ , $ ans $ 作为单位矩阵,需要保持不变

无向图得到的是一个对称阵,运算一下发现,刚好对角线为 $ 0 $ 的时候才满足

PNG图像image-20210212185307461

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <bits/stdc++.h>
#define ll long long
namespace FAST_IO
{
template <class T>
inline void read(T &x)
{
T flag = 1;
x = 0;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
flag = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
}
x *= flag;
}

template <class T, class... _T>
inline void read(T &x, _T &...y)
{
return read(x), read(y...);
}

} // namespace FAST_IO
using namespace std;
using namespace FAST_IO;
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;
const double eqs = 1e-5;
const int maxn = 3e2 + 10;
const int maxm = 1e5 + 10;
// 使用的时候注意一下矩阵乘法自带mod,矩阵的len,type等
struct Matrix
{
using type = ll;
static const int len = maxn;
type a[len][len];
int m, n; // 矩阵的行列
int dim; // 矩阵的维度
Matrix(int n, int m)
{
this->m = m, this->n = n;
clean();
}
Matrix(int n)
{
this->m = this->n = n;
clean();
}
Matrix(int n, char ch)
{
this->m = this->n = n;
if (ch == 'i' || ch == 'I')
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = (i == j);
}
else if (ch == 'u' || ch == 'U')
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = INF_LL;
}
}

void clean()
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = 0;
}
type *operator[](const int x)
{
return a[x];
}

Matrix operator=(const Matrix &a)
{

for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
this->a[i][j] = a.a[i][j];
}
}
return *this;
}

Matrix operator%(const ll &mod) const
{
Matrix ret(n, m);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
ret.a[i][j] = a[i][j] % mod;
}
}
return ret;
}

Matrix operator%=(ll mod)
{
*this = *this % mod;
return *this;
}

Matrix operator*(const Matrix &a) const
{
Matrix ret(n, 'u');
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= a.m; j++)
{
for (int k = 1; k <= m; k++)
{
// ret.a[i][j] = (ret.a[i][j] + this->a[i][k] * a.a[k][j] % mod) % mod;
ret.a[i][j] = min(ret.a[i][j], (this->a)[i][k] + a.a[k][j]);
}
}
}
return ret;
}
Matrix operator*=(const Matrix &a)
{
*this = (*this) * a;
return *this;
}

Matrix qpow(ll k)
{
Matrix ans(n, 'u'), tmp = *this;
for (int i = 1; i <= n; i++)
ans[i][i] = 0;
while (k)
{
if (k & 1)
ans = ans * tmp;
tmp = tmp * tmp;
k >>= 1;
}
return ans;
}
};

int n, t, s, e;
map<int, int> id;
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
read(n, t, s, e);
int cnt = 0;
Matrix g(t << 1, 'u');
for (int i = 1, u, v, w; i <= t; i++)
{
read(w, u, v);
if (!id.count(u))
id[u] = ++cnt;
if (!id.count(v))
id[v] = ++cnt;
int uu = id[u], vv = id[v];
g[uu][vv] = g[vv][uu] = min((ll)w, g[uu][vv]);
}
g.n = g.m = cnt;
g = g.qpow(n);
printf("%lld\n", g[id[s]][id[e]]);
return 0;
}