次短路
思路:拆点
将一个节点 \(node\) 拆为 \(node[0]\) 和 \(node[1]\),其中 \(node[0]\) 是 \(node\) 的最短路,\(node[1]\) 是 \(node\) 的次短路,如果不拆点的话,那么每个 \(node\) 只会出队更新其他节点一次(即st[node]=true;
此时 \(node\) 要么是满足最短路要么满足次短路),然而事实是,次短路和最短路都可能用来更新其他节点,因此我们需要st[node][0]=true;/st[node][1]=true;
在 \(dijkstra\) 和 \(dfs\) 中次短路也满足拓扑序,因此我们可以用 \(dijkstra\) 来求解权值不为 \(1\) 的问题,注意不能直接用 \(spfa\),因为 \(spfa\) 不满足拓扑序,除非我们先用 \(top_sort\) 预处理,但那样太麻烦。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1010, M = 10010;
typedef struct ver_t {
// type=0 --> 最短路
// type=1 --> 次短路
int distance, ver, type;
bool operator<(const ver_t &x) const {
return distance > x.distance;
}
} Ver;
int n, m, head, tail;
int h[N], e[M], ne[M], w[M], idx;
int dist[N][2], cnt[N][2];
bool st[N][2];
void add(int a, int b, int c)
{
w[idx] = c;
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra()
{
// 0 --> 最短路
// 1 --> 次短路
memset(dist, 0x3f, sizeof dist);
memset(cnt, 0, sizeof cnt);
memset(st, false, sizeof st);
dist[head][0] = 0;
cnt[head][0] = 1;
priority_queue<Ver> q;
q.push({dist[head][0], head, 0});
while(q.size())
{
auto [distance, ver, type] = q.top();
q.pop();
if(st[ver][type]) continue;
st[ver][type] = true;
for(int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
/*小 -------------------------------------> 大
dist[j][0] dist[j][1]
ne_dist ne_dist ne_dist ne_dist (四种情况)*/
int ne_dist = distance + w[i];
if(ne_dist < dist[j][0]) // 比最短路还要小
{
dist[j][1] = dist[j][0];
cnt[j][1] = cnt[j][0];
dist[j][0] = ne_dist;
cnt[j][0] = cnt[ver][type];
q.push({dist[j][0], j, 0});
q.push({dist[j][1], j, 1});
}
else if(ne_dist == dist[j][0]) // 和最短路一样长
cnt[j][0] += cnt[ver][type];
else if(ne_dist < dist[j][1]) // 比次短路长
{
dist[j][1] = ne_dist;
cnt[j][1] = cnt[ver][type];
q.push({dist[j][1], j, 1});
}
else if(ne_dist == dist[j][1]) // 和次短路一样长
cnt[j][1] += cnt[ver][type];
}
}
return cnt[tail][0] + cnt[tail][1] * (dist[tail][1] == dist[tail][0] + 1);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while(T -- )
{
memset(h, -1, sizeof h), idx = 0;
cin >> n >> m;
while(m -- )
{
int a, b, c; cin >> a >> b >> c;
add(a, b, c);
}
cin >> head >> tail;
cout << dijkstra() << endl;
}
return 0;
}