2022杭电多校第一场 Path

Problem - 7145 Path

题意:给定一张 \(n\) 个点 \(m\) 条边的图,其中每条边有边权和是否为特殊边的标记。如果你走过的是特殊边,那么在你走过特殊边后的下一步,你可以选择到达任意一个点,如果这个点你原来是不能直接到达的,那么花费就是0,否则就是 \(w_i-K\) 其中 \(w_i\) 表示走过特殊边后的那条边的权值。问到达每个点的最小花费是多少。

知识点:分层图,最短路

首先我们考虑一个比较暴力的做法,建立两层分层图,其中下面一层表示通过普通边到的顶点,上面表示通过特殊边到达的顶点,那么可以从上边向下面所有不是直接相连的边连一条 \(0\) 的边,然后直接跑最短路即可,这样是 \(n^2\) 的,所以我们考虑优化。

注意到,我们在最短路的过程中,更新的过程总是越来越远的,也就是后出队的点的距离总是大于先出队的点的距离

换句话说,最短路的更新过程总是从近到远,先更新完成的总是近点,然后才是远点,这就导致了每次我们取出队列中一个点,当这个点的距离是更新完成时,这些点组成的序列总是递增的,

也就是说,假设我们刚刚经过了一条特殊边到达了点 \(u\),记作 \(dis[u][1]\),假设我利用这个点更新了一个非邻点 \(v\) 记作 \(dis[v][0]\),我们假设我们后续又用一个点 \(t\) 更新了点 \(v\),也就是

\(dis[v][0] = dis[u][1] + 0\)

\(dis[v][0] = dis[t][0/1] + w\)

但是根据我们上面得到的结论,我们可以知道,之后拿来更新的 \(dis[t][0/1] \ge dis[u][1]\) ,所以只要是从 \(dis[u][1]\) 更新的点,都是已经是更新完成了的,每个点只会被这样更新一次,我们只需要用一个集合维护这样的点即可。

这题的重点就是理解我上面说的结论,如果不太懂的可以去看看dijkstra球最短路是怎么推的,这里就不再赘述。

#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
const LL INF = 1e18;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

struct node {
    int nex;
    int val, ops;
};
struct edge {
    LL dis;
    int idx, ops;
    bool operator < (const edge &B) const {
        return dis > B.dis;
    }
};
inline void solve() {
    int n, m; cin >> n >> m;
    int S, K; cin >> S >> K;
    vector<node> g[n + 1];
    while (m -- ) {
        int u, v, w, ops;
        cin >> u >> v >> w >> ops;
        g[u].push_back({v, w, ops});
    }
    set<int> s;
    for (int i = 1; i <= n; i ++ ) if (i != S) s.insert(i);
    priority_queue<edge, vector<edge>> q;
    vector<vector<LL>> dis(n + 1, vector<LL>(2, INF));
    vector<vector<bool>> vis(n + 1, vector<bool>(2));
    vector<bool> st(n + 1);
    dis[S][0] = 0;
    q.push({dis[S][0], S, 0});
    LL last = 0;
    while (!q.empty()) {
        auto t = q.top(); q.pop();
        int u = t.idx, ops = t.ops;
        if (ops == 1) {
            for (auto ite : g[u]) st[ite.nex] = true;
            vector<int> tmp;
            for (auto ite : s) {
                if (st[ite]) continue;
                dis[ite][0] = dis[u][1];
                q.push({dis[ite][0], ite, 0});
                tmp.push_back(ite);
            }
            for (auto ite : tmp) s.erase(ite);
            for (auto ite : g[u]) st[ite.nex] = false;
        } else s.erase(u);

        if (vis[u][ops]) continue;
        vis[u][ops] = true;
        assert(dis[u][ops] >= last);
        last = dis[u][ops];
        for (auto ite : g[u]) {
            int v = ite.nex, w = ite.val - (ops == 1 ? K : 0);
            if (dis[v][ite.ops] > dis[u][ops] + w) {
                dis[v][ite.ops] = dis[u][ops] + w;
                q.push({dis[v][ite.ops], v, ite.ops});
            }
        }
    }
    for (int i = 1; i <= n; i ++ ) {
        LL res = min(dis[i][0], dis[i][1]);
        if (res == INF) cout << -1 << ' ';
        else cout << res << ' ';
    }
    cout << endl;
}
signed main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
    int T; cin >> T;
    while (T -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}

posted @ 2022-08-04 20:51  Time_Limit_Exceeded  阅读(30)  评论(0编辑  收藏  举报