P1144 题解
洛谷 P1144 最短路计数
题意
给出一个 \(n\) 个顶点 \(m\) 条边的无向无权图,顶点编号为 \(1 \sim n\)。 问从顶点 \(1\) 开始,到其他每个点的最短路有几条。
\(1 \le n \le 10 ^ 6, 1 \le m \le 2 \times 10 ^ 6\)
。
思路
首先,这个题目时非常明显是搜索的。
然后,看见 \(n\) 有 \(10 ^ 6\) 这么大,又是最短路,肯定不能考虑深搜,那就只能考虑广搜了。
最短路应该都会搜,那么,问题就在,怎么记录最短路呢?
首先,我们先写一遍最短路。
void Record(int u, int x, int step) {
if (d[x]) { // 如果被遍历过
return ;
}
d[x] = step, que.push(x); // 记录答案,加入队列
}
void bfs() {
for (Record(0, 1, 1); !que.empty(); ) {
int u = que.front(); // 取出队头
que.pop();
for (int i = 0; i < f[u].size(); i++) {
Record(u, f[u][i], d[u] + 1); // 转移
}
}
}
现在的确找到最短路了,但是,要怎么求数量呢?
可以发现,1 -> 2 -> 4
和 1 -> 3 -> 4
的长度都是 \(3\) ,都是最短路,所以在统计答案的时候又加上了 \(1\) 到 \(3\) 的路径数量。
那么,就可以这样来分类:
-
如果当前点没有被遍历过:直接记录答案,将当前元素加入队列。
-
如果当前点被遍历过:设当前点是从点 \(x\) 转移而来的,就加上 \(ans_x\)。
所以,代码就很简单了。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, a, b;
int d[N], ans[N];
queue<int> que;
vector<int> f[N];
void Record(int u, int x, int step) {
if (d[x]) {
if (step == d[x]) { // 如果当前是最短路
ans[x] = (ans[u] + ans[x]) % 100003; // 统计答案
}
return ;
}
d[x] = step, ans[x] = ans[u]; //记录答案
que.push(x); // 放入队列
}
void bfs() {
ans[0] = 1; // 将虚拟点赋值答案
for (Record(0, 1, 1); !que.empty(); ) {
int u = que.front(); // 取出队头
que.pop();
for (int i = 0; i < f[u].size(); i++) { //转移
Record(u, f[u][i], d[u] + 1);
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
while (m--) {
cin >> a >> b;
// 建图
f[a].push_back(b);
f[b].push_back(a);
}
bfs();
for (int i = 1; i <= n; i++) {
cout << ans[i] % 100003 << '\n';
}
return 0;
}