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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值