次短路

光風霽月發表於2024-03-09

題目連結

思路:拆點

將一個節點 \(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;
}