BZOJ1576: [Usaco2009 Jan]安全路經Travel 最短路徑樹+並查集

不難發現求出最短路徑樹後,每一條非樹邊(u, v) 可以用來更新u~lca(u, v), v~lca(u,v)這兩條鏈上的點
對於一個可被更新的點x,他的答案可以被更新爲dis[u] + dis[v] + 該非樹邊的邊權 - dis[x]
似乎和dis[x]無關 那麼可以按dis[u] + dis[v] + 邊權給非樹邊排序 取最小的來更新就好
這裏有一個並查集更新鏈上結點信息的姿勢:令並查集維護的每個集合除代表元外都已被更新,我們就可以像樹剖求lca一樣每次跳到並查集的代表元上並更新它,保證不重不漏
200000*100000 spfa可能會爆

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 100007, maxm = 400007;
int eid, eid2, res[maxn], d[maxn], fa[maxn], head[maxn], f[maxn], n, m;
struct edge{
    int v, nxt, w;
}e[maxm];
struct _{
    int w, v, u;
    bool operator < (const _ &rhs) const {
        return w < rhs.w;
    }
}e2[maxm];
void init(){memset(head, -1, sizeof(head));eid = 0;}
void insert(int u, int v, int w){
    e[eid].v = v; e[eid].w = w; e[eid].nxt = head[u]; head[u] = eid++;
}
void adde(int u, int v, int w) {
    insert(u, v, w); insert(v, u, w);
}
struct __{
    int u, d;
    bool operator < (const __ &rhs) const {
        return d > rhs.d;
    }
    __ (int _u, int _d) : u(_u), d(_d) {}
};
bool vis[maxn];
priority_queue<__> min_heap;
void dij(){
    memset(d, 0x3f, sizeof(d));
    min_heap.push(__(1, d[1] = 0));
    while(!min_heap.empty()){
        int u = min_heap.top().u;
        min_heap.pop();
        if (vis[u]) continue;
        vis[u] = true;
        for (int i = head[u]; ~i; i = e[i].nxt){
            int v = e[i].v;
            if (d[v] > d[u] + e[i].w){
                d[v] = d[u] + e[i].w;
                f[v] = u;
                min_heap.push(__(v, d[v]));
            }
        }
    }
} 
int get(int u){return fa[u] == u ? u : fa[u] = get(fa[u]);}
int read(){
    int s = 0, f = 1; char c = getchar();
    while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') {
        s = s * 10 + c - '0'; c = getchar();
    }
    return s * f;
}
int main(){
    n = read(), m = read();
    init();
    for (int i = 1; i <= m; i++){
        int u = read(), v = read(), w = read();
        adde(u, v, w);
        e2[i].u = u; e2[i].v = v; e2[i].w = w;
    }
    dij();
    for (int i = 1; i <= m; i++) e2[i].w += d[e2[i].u] + d[e2[i].v];
    sort(e2 + 1, e2 + m + 1);
    for (int i = 1; i <= n; i++) fa[i] = i;
    memset(res, -1, sizeof(res));
    int u, v, w;
    for (int i = 1; i <= m; i++) {
        u = e2[i].u; v = e2[i].v; w = e2[i].w;
        if (u == f[v] || v == f[u]) continue;   //樹邊
        u = get(u), v = get(v);
        while(u != v){
            if (d[u] < d[v]) swap(u, v);
            res[u] = w - d[u];
            fa[u] = v;
            u = get(f[u]);
        }
    }
    for (int i = 2; i <= n; i++) printf("%d\n", res[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章