题目大意
给定一个 $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 |  | 
2 |  | 
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 | } |