1576: [Usaco2009 Jan]安全路经Travel

第一次看到用最短路树来解题,一下针对这种某些边不能选,次优值之类的最短路除了A*之外的套路.
往往可以求全局,在线可以用LCT来维护

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5;
const int M = N * 2;

int fir[N] , ne[M] , to[M] , cnt, dis[N] , hu[N] , x , y , z,  C[M] , pre[N] , n , m , fa[N] , st[N][26] , dep[N];
bool vis[N];

void add(int x , int y , int z) {
    ne[++ cnt] = fir[x]; fir[x] = cnt; to[cnt] = y; C[cnt] = z;
}

void link(int x , int y , int z) {
    add(x , y , z); 
//  cerr << cnt <<" adasd" << endl;
    add(y , x , z);
//  cerr << cnt <<" adasd" << endl;
}

struct NODE {
    int val , id , Pre;
    friend bool operator < (NODE xxx , NODE yyy) {
        return xxx.val > yyy.val;
    }
}tmp , ind;

priority_queue<NODE>q;

#define Foreachson(i , x) for(int i = fir[x];i;i = ne[i])

vector<int>G[N];

void dfs(int x , int f) {
    dep[x] = dep[f] + 1; pre[x] = f;
    st[x][0] = f; for(int i = 1;i < 21;i ++) st[x][i] = st[st[x][i - 1]][i - 1];
    for(int i = 0;i < (int) G[x].size();i ++) {
        dfs(G[x][i] , x);
    }
}

int getf(int x) {
    return (x == fa[x]) ? x : (fa[x] = getf(fa[x]));
}

struct push {
    int x , y , val;
    friend bool operator < (push xxx , push yyy) {
        return xxx.val < yyy.val;
    }
}P[N];


int LCA(int x , int y) {
    if(dep[x] > dep[y]) swap(x , y);
    for(int i = 20;i >= 0;i --) if(dep[st[y][i]] >= dep[x]) y = st[y][i];
    if(x == y) return x;
    for(int i = 20;i >= 0;i --) if(st[x][i] != st[y][i]) x = st[x][i] , y = st[y][i];
    return st[x][0];
}

int main(void) {
//  freopen("data.txt" , "r" , stdin);
//  freopen("myout.txt" ,"w" , stdout);
    scanf("%d%d" , &n , &m); cnt ++;
    for(int i = 1;i <= m;i ++) {
        scanf("%d%d%d" , &x , &y , &z); link(x , y , z);
    }
    memset(hu , 127  , sizeof(hu));
    q.push((NODE){0 , 1 , 0});
    while(!q.empty()) {
        while(!q.empty() && vis[q.top().id]) q.pop();
        if(q.empty()) break;
        ind = q.top(); q.pop();
        vis[ind.id] = 1; dis[ind.id] = ind.val; pre[ind.id] = ind.Pre;
        Foreachson(i , ind.id) {
            int V = to[i]; if(vis[V]) continue;
            q.push((NODE){ind.val + C[i] , V , ind.id});
        }
    }
    for(int i = 1;i <= n;i ++) fa[i] = i;
    for(int i = 1;i <= n;i ++) G[pre[i]].push_back(i);
    dfs(1 , 0);
    int tot = 0;
    for(int i = 2;i <= cnt;i += 2) {
        int u = to[i] , v = to[i ^ 1] , w = C[i];
        if(pre[u] == v || pre[v] == u) continue;
        P[++ tot].x = u; P[tot].y = v; P[tot].val = dis[u] + dis[v] + C[i];
    }
    sort(P + 1 , P + tot + 1);
    for(int i = 1;i <= tot;i ++) {
        if(getf(P[i].x) == getf(P[i].y)) continue;
        int fx = getf(P[i].x) , fy = getf(P[i].y);
        int lca = getf(LCA(fx , fy));
        while(fx != lca) {
            hu[fx] = P[i].val - dis[fx];
            fa[fx] = pre[fx]; fx = getf(fx);
        }
        while(fy != lca) {
            hu[fy] = P[i].val - dis[fy];
            fa[fy] = pre[fy]; fy = getf(fy);
        }
    }
    for(int i = 2;i <= n;i ++) {
        if(hu[i] > 4e8) puts("-1");
        else printf("%d\n" , hu[i]);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值