数据结构与算法MOOC / 第十二章 高级数据结构 练习题 9:Training little cats(不构造矩阵的终极优化)
题目链接
AC代码
#include <cstdio>
#define swap(a, b) if ((a) != (b)) (a) ^= (b), (b) ^= (a), (a) ^= (b)
typedef unsigned long long uint64;
struct Matrix {
uint64 b[101];
int A[101];
} mat, matBuff;
uint64 results[101], buff[101], n, K, a, b, m;
char cmd[2];
void setMAT() {
for (int i = 0; i <= n; ++i) {
results[i] = 0;
buff[i] = 0;
mat.A[i] = i;
mat.b[i] = 0;
}
for (int i = 0; i < K; ++i) {
scanf("%s", cmd);
switch (*cmd) {
case 'g':
scanf("%llu", &a);
++mat.b[a];
break;
case 'e':
scanf("%llu", &b);
mat.A[b] = 0;
mat.b[b] = 0;
break;
case 's':
scanf("%llu%llu", &a, &b);
swap(mat.A[a], mat.A[b]);
swap(mat.b[a], mat.b[b]);
break;
default:
break;
}
}
}
void matLeftMul(Matrix a, Matrix &b) {
matBuff.A[0] = 0, matBuff.b[0] = 0;
for (int i = 1; i <= n; ++i) {
matBuff.A[i] = b.A[a.A[i]];
matBuff.b[i] = a.b[i] + b.b[a.A[i]];
}
for (int i = 1; i <= n; ++i) {
b.A[i] = matBuff.A[i];
b.b[i] = matBuff.b[i];
}
}
void vecLeftMul(Matrix a, uint64 *b) {
*buff = 0;
for (int i = 1; i <= n; ++i)
buff[i] = a.b[i] + b[a.A[i]];
for (int i = 1; i <= n; ++i)
b[i] = buff[i];
}
void calcResults() {
while (m) {
if (m & 1) vecLeftMul(mat, results);
matLeftMul(mat, mat);
m >>= 1;
}
}
int main() {
while (scanf("%llu%llu%llu", &n, &m, &K), n | m | K) {
setMAT();
calcResults();
for (int i = 1; i <= n; ++i)
printf("%llu ", results[i]);
putchar('\n');
}
}
解题思路
这是一道经典的问题,来自北京大学2009年校内赛。很早以前见过这道题,然后在查到了很多构造矩阵的方法。感觉北大挺喜欢数学类的问题的(毕竟是主打理科)。
这是笔者第一次写快速幂算法,写起挺快的,然后果不其然地就掉入稀疏矩阵的陷阱,被恭喜TLE,然后被提示需要对稀疏矩阵的乘法进行优化。发现网上大多数优化方法都很简单,记录一下每一行每一列是否全0,这样求解的时间代价从 O ( n 3 log m ) O(n^3 \log m) O(n3logm)变为了 O ( n 2 log m ) O(n^2 \log m) O(n2logm),就可以AC了。但是既然是稀疏矩阵,正好最近刷了些图论的题,就想试试用十字链表练练手吧。写着写着,突然灵光一现,发现了一个进一步把时间代价优化到 O ( n log m ) O(n \log m) O(nlogm)的方法。
变换矩阵的构造
根据恭喜我TLE的那篇文章的说法,我们可以把原问题写成这样的形式:
( A b O 1 ) m × ( O 1 ) = ( x ′ 1 ) \begin{pmatrix} A & b \\ O & 1 \end{pmatrix}^m \times \begin{pmatrix} O \\ 1 \end{pmatrix} = \begin{pmatrix} x' \\ 1 \end{pmatrix} (