1 条题解

  • 0
    @ 2026-10-26 17:31:10

    思路与算法

    分别求A出发、到B、C出发、到D的四组最短距离。筛出同时位于两人最短路线中的点和边。正权下这些公共边按A距离严格递增,构成DAG;每个公共点可作起点计1,按距离排序做最长路节点数DP。任一原始目的地不可达时输出-1。

    正确性说明

    任一可行会合序列必须按同一顺序出现在双方最短路上。两个相邻会合点之间双方子路都是它们之间的最短路,故可替换为同时满足两人最短等式的公共子路。因此会合序列可包含在公共DAG的一条路上。反之公共路可接各人的最短前后缀,并用休息同步所有会合点,所以最长节点数可达且最优。

    复杂度

    建正反图O(N+M),四次Dijkstra O((N+M)log(N+M)),排序O(NlogN)、DAG转移O(N+M),总空间O(N+M)。距离可达约2.5×10^13,使用64位。

    易错点

    公共节点不一定从共同起点可达,DP应允许任何公共点起步。没有公共点答案0;任一行程无路才为-1。正权是按距离拓扑的依据。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using Edge = pair<int, long long>;
    vector<long long> dij(int s, const vector<vector<Edge>> &g) {
        const long long INF = 1LL << 60;
        vector<long long> d(g.size(), INF);
        priority_queue<pair<long long, int>, vector<pair<long long, int>>,
                       greater<pair<long long, int>>>
            q;
        d[s] = 0;
        q.push({0, s});
        while (!q.empty()) {
            auto t = q.top();
            q.pop();
            if (t.first != d[t.second])
                continue;
            for (auto e : g[t.second])
                if (d[e.first] > t.first + e.second) {
                    d[e.first] = t.first + e.second;
                    q.push({d[e.first], e.first});
                }
        }
        return d;
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<vector<Edge>> g(n), h(n);
        vector<tuple<int, int, long long>> es;
        while (m--) {
            int a, b;
            long long w;
            cin >> a >> b >> w;
            --a;
            --b;
            g[a].push_back({b, w});
            h[b].push_back({a, w});
            es.emplace_back(a, b, w);
        }
        int A, B, C, D;
        cin >> A >> B >> C >> D;
        --A;
        --B;
        --C;
        --D;
        auto a = dij(A, g), b = dij(B, h), c = dij(C, g), d = dij(D, h);
        if (a[B] == (1LL << 60) || c[D] == (1LL << 60)) {
            cout << -1 << '\n';
            return 0;
        }
        vector<int> ok(n), dp(n), ord(n);
        for (int i = 0; i < n; i++) {
            ok[i] = a[i] + b[i] == a[B] && c[i] + d[i] == c[D];
            dp[i] = ok[i];
        }
        iota(ord.begin(), ord.end(), 0);
        sort(ord.begin(), ord.end(), [&](int u, int v) {
            return a[u] < a[v];
        });
        int ans = 0;
        for (int u : ord) {
            ans = max(ans, dp[u]);
            if (!ok[u])
                continue;
            for (auto e : g[u])
                if (ok[e.first] && a[u] + e.second == a[e.first] && c[u] + e.second == c[e.first])
                    dp[e.first] = max(dp[e.first], dp[u] + 1);
        }
        cout << ans << '\n';
    }
    
    • 1

    信息

    ID
    1808
    时间
    2000ms
    内存
    256MiB
    难度
    普及+
    标签
    递交数
    1
    已通过
    1
    上传者