G. Counting Shortcuts
题目大意: 给定一个由 n 个点和 m 条边组成的无向图(所有边权为1),并给定起点和终点,求与最短路径差不超过1的路径条数。
思路: 因为所有边权为1,所以答案为起点到终点的最短路径数量和次短路径数量之和。
具体做法: BFS求得起点到所有点的最短路并计数,因为所有边权为1,所以起点到某个点的次短路有两种情况:
- 与该点相邻并且最短路径与到该点的最短路径相等的点;
- 在起点到该点的最短路径上的点(要求走的是次短路)。
设 d i s [ i ] dis[i] dis[i] 为起点到 i 点的最短路径, c n t 1 [ i ] cnt1[i] cnt1[i] 为最短路径数量, c n t 2 [ i ] cnt2[i] cnt2[i] 为次短路经数量,则:
if(dis[i] == dis[j]) {
cnt2[i] += cnt1[j];
}
if(dis[i] == dis[j] + 1) {
cnt2[i] += cnt2[j];
}
此外,需要特别强调的是,第二种情况需要按最短路径从小到大进行转移。
代码:
constexpr int P = 1e9 + 7;
void solve() {
int n, m, s, t;
cin >> n >> m >> s >> t;
vector<vector<int>> adj(n + 1);
for(int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<ll> dis(n + 1, -1), cnt1(n + 1), cnt2(n + 1);
vector<bool> vis(n + 1);
queue<int> q;
q.push(s);
dis[s] = 0;
cnt1[s] = 1;
while(!q.empty()) {
int u = q.front();
q.pop();
for(auto v : adj[u]) {
if(dis[v] == -1) {
dis[v] = dis[u] + 1;
q.push(v);
}
if(dis[v] == dis[u] + 1) {
cnt1[v] = (cnt1[v] + cnt1[u]) % P;
}
}
}
for(int i = 1; i <= n; ++i) {
for(auto j : adj[i]) {
if(dis[j] == dis[i]) {
cnt2[i] = (cnt2[i] + cnt1[j]) % P;
}
}
}
vector<pair<int, int>> b(n + 1);
for(int i = 1; i <= n; ++i) {
b[i] = make_pair(dis[i], i);
}
sort(b.begin(), b.end());
for(int i = 1; i <= n; ++i) {
int u = b[i].second;
for(auto v : adj[u]) {
if(dis[u] == dis[v] + 1) {
cnt2[u] = (cnt2[u] + cnt2[v]) % P;
}
}
}
cout << (cnt1[t] + cnt2[t]) % P << "\n";
}