ABC236G

对于一个 \(p\times m\) 的矩阵 \(A\),与 \(m\times q\) 的矩阵 \(B\),定义广义矩阵乘法 \(A\times B=C\) 的结果是一个 \(p\times q\) 的矩阵 \(C\),满足:

\[C_{i,j}=(A_{i,1}\otimes B_{1,j})\oplus(A_{i,2}\otimes B_{2,j})\oplus\cdots\oplus(A_{i,n}\otimes B_{n,j}) \]

其中 \(\otimes\)\(\oplus\) 是两种二元运算。

一些约定:

  • \(\otimes\) 有交换律:\(a\otimes b=b\otimes a\)
  • \(\otimes\) 有结合律:\((a\otimes b)\otimes c=a\otimes(b\otimes c)\)
  • \(\otimes\)\(\oplus\) 有分配律:\(a\otimes (b\oplus c)=(a\otimes b)\oplus (a\otimes c)\)

\(\otimes\) 有交换律,结合律,\(\otimes\)\(\oplus\) 有分配律,那么广义的矩阵乘法就满足结合律。

下面证明 \(\otimes=\max,\oplus=\min\) 的广义矩阵乘法具有结合律:

首先显然 \(\max\) 有交换律和结合律,那么 \(\max(a,\min(b,c))=\min(\max(a,b),\max(a,c))\) 也是显然的。

回到原问题,令 \(f(v,i)\) 表示从 \(1\) 出发,经过 \(i\) 条边到达点 \(v\) 的最早时间,\(w(u,v)\) 表示边 \(u\to v\) 被加入的最小时间,可以得到转移:

\[f(v,i)=\min\limits_{1\le u\le n}\left\{\max(f(u,i-1),w(u,v))\right\} \]

其中 \(f(1,0)=0\),其余均为 \(\infty\)

不难发现这个由上面的结论可以矩阵快速幂优化:

\[\begin{bmatrix} f(1,i)\\ f(2,i)\\ f(3,i)\\ \vdots \\ f(n,i) \end{bmatrix}=\begin{bmatrix} w(1,1), w(2,1),\cdots,w(n,1) \\ w(1,2), w(2,2),\cdots,w(n,2) \\ w(1,3), w(2,3),\cdots,w(n,3) \\ \ddots \\ w(1,n), w(2,n),\cdots,w(n,n) \end{bmatrix} \begin{bmatrix} f(1,i-1)\\ f(2,i-1)\\ f(3,i-1)\\ \vdots \\ f(n,i-1) \end{bmatrix}\]

时间复杂度 \(\mathcal O(n^3\log L)\)

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 105, inf = 0x3f3f3f3f;
int n, m, L;
struct mat {
	int a[N][N];
	
	mat operator * (const mat &x) const {
		mat res; memset(res.a, 0x3f, sizeof res.a);
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j)
				for (int k = 1; k <= n; ++k)
					res.a[i][j] = min(res.a[i][j], max(a[i][k], x.a[k][j]));
		return res;
	}
} f;

mat qpow(mat x, int y) {
	mat res; memset(res.a, 0x3f, sizeof res.a);
	for (int i = 1; i <= n; ++i) res.a[i][i] = 0;
	while (y) {
		if (y & 1) res = res * x;
		x = x * x;
		y >>= 1;
	}
	return res;
}

int main() {
	scanf("%d%d%d", &n, &m, &L);
	memset(f.a, 0x3f, sizeof f.a);
	for (int i = 1, u, v; i <= m; ++i) {
		scanf("%d%d", &u, &v);
		f.a[u][v] = min(f.a[u][v], i);
	}
	f = qpow(f, L);
	for (int i = 1; i <= n; ++i) printf("%d ", f.a[1][i] != inf ? f.a[1][i] : -1);
	return 0;
}
posted @ 2022-11-09 14:50  Kobe303  阅读(18)  评论(0编辑  收藏  举报