只要你跑的够快,锅就追不上你

0%

「BZOJ 5056」OI 游戏(最短路 + 矩阵树定理)

题目大意

「BZOJ 5056」OI 游戏

给定一个 $n$ 个结点 $m$ 条边的带权无向图,所有边权都是正数。问图中有多少生成树,满足 $1$ 号点到每个点的最短路都等于在树上它到那个结点的距离。

数据范围:$n \le 50, m \le 1225$。

思路分析

我们发现这棵树一定是最短路 DAG 上的一棵生成树,于是我们建出最短路 DAG,再使用矩阵树定理求该 DAG 以 $1$ 为根的外向树个数即可。时间复杂度 $O(n^3)$。

然而这题的数据范围其实是可以出到 $n, m \le 10^6$ 的。我们发现最短路 DAG 有很好的性质。首先它一定是一个 DAG;其次,它入度为 $0$ 的点只有 $1$ 号结点。那么我们就可以给除了 $1$ 号点的每个点选一个父亲结点,然后把方案数相乘就可以得到答案。时间复杂度 $O(n \log n)$。

代码实现

1
#include <cstdio>
2
#include <algorithm>
3
using namespace std;
4
5
const int maxn = 50, inf = 1e9 + 1, mod = 1e9 + 7;
6
int n, a[maxn + 3][maxn + 3], f[maxn + 3][maxn + 3], deg[maxn + 3];
7
8
int main() {
9
	scanf("%d", &n);
10
	for (int i = 1; i <= n; i++) {
11
		for (int j = 1; j <= n; j++) {
12
			scanf("%1d", &a[i][j]);
13
			if (i != j && a[i][j] == 0) {
14
				a[i][j] = inf;
15
			}
16
			f[i][j] = a[i][j];
17
		}
18
	}
19
	for (int k = 1; k <= n; k++) {
20
		for (int i = 1; i <= n; i++) {
21
			for (int j = 1; j <= n; j++) {
22
				f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
23
			}
24
		}
25
	}
26
	for (int i = 1; i <= n; i++) {
27
		for (int j = 1; j <= n; j++) {
28
			if (i != j && f[1][i] + a[i][j] == f[1][j]) {
29
				deg[j]++;
30
			}
31
		}
32
	}
33
	int ans = 1;
34
	for (int i = 2; i <= n; i++) {
35
		ans = 1ll * ans * deg[i] % mod;
36
	}
37
	printf("%d\n", ans);
38
	return 0;
39
}