[BZOJ1576] [Usaco2009 Jan]安全路经Travel

题意

N M边, N1 头牛从 1 沿最短路径走到对应的2...N N1 个点。询问避开牛经过的最后一条边到达相应点的最短路径长度。输出这 N1 个长度。

题解

我们暂且称避开最短路最后一条边的路径为“次短路”(不是真正的次短路)。
首先将最短路经过的最后一条边都求出来,这些边将形成一棵以1为根的树。
考虑到达点u的答案。若避开点u与fa[u]中的这条边,那么次短路必然是从点1经过最短路到达某点v,从v经过非树边到达点w,从点w沿着最短路向根的方向找到u(即w为u-子树中的节点)。

如图,图(1)中是一种合法的情况,而图(2)中v与w都在u的子树中,这显然是不合法的。即对于所有的非树边的两个端点,若有且仅有一个端点在u的子树中(包含u)就能更新u的答案。如果这样,每个点u都可以由一个非树边的集合来更新它,如果枚举肯定不能接受。观察一条非树边[v,w]如何更新u的答案:

ans[u]=dis[v]+len[v,w]+dis[w]dis[u]

ans[u]dis[u]=dis[v]+len[v,w]+dis[w]

我们最小化 dis[v]+len[v,w]+dis[w] 就可最小化ans[u],于是很容易想到用堆维护这个边集,再自下而上地合并堆,这样就可以由下而上得到每个点的非树边边集。每次更新时将端点不满足有且仅有一个在子树中的非树边都删掉。
如果进行堆的合并,优先队列是 O(n) 的,而左偏树是 O(logn) 的,所以选择左偏树编写。

代码

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

struct ltnode {
    int w, u, v;
    bool operator < (const ltnode&B) const { return w > B.w; }
};

struct lt {
    lt *l, *r;
    int d;
    ltnode w;
    lt();
    lt(ltnode w);
}*null=new lt();
lt::lt() { l=r=null;d=0; }
lt::lt(ltnode w): w(w) { l=r=null;d=0; }
lt* Init(ltnode x) { return new lt(x); }
lt* Merge(lt*u,lt*v) {
    if (u == null) return v;
    if (v == null) return u;
    if (u->w < v->w) std::swap(u,v);
    u->r = Merge(u->r,v);
    if (u->l->d < u->r->d) std::swap(u->l,u->r);
    u->d = u->r->d+1;
    return u;
}
void Insert(lt*&u,ltnode x) { u = Merge(u,Init(x)); }
ltnode Top(lt*u) { return u->w; }
void Pop(lt*&u) { lt*tmp = u; u = Merge(u->l,u->r); delete tmp; }
bool Empty(lt*u) { return u == null; }

#define  kN  100010LL
#define  kM  200010LL
#define  infi  0x3f3f3f3fLL
#define  t(p) to[p]
#define  l(p) len[p]
#define  n(p) nxt[p]
#define  s(u) st[u]

int to[kM<<1], len[kM<<1], nxt[kM<<1], st[kN], te = 1;
inline void Add(int u,int v,int w) {
    te++;t(te)=v;l(te)=w;n(te)=s(u);s(u)=te;
    te++;t(te)=u;l(te)=w;n(te)=s(v);s(v)=te;
}

int dis[kN], pre[kN];
bool inq[kN];
std::deque<int>q;
inline void spfa() {
    int u, v, p;
    memset(dis,0x3f,sizeof dis);
    q.push_back(1), dis[1] = 0;
    while (!q.empty())
        for (u=q.front(),q.pop_front(),inq[u]=false,p=s(u);p;p=n(p))
            if (v=t(p),dis[v]>dis[u]+l(p))
                if (dis[v]=dis[u]+l(p),pre[v]=u,!inq[v])
                    if (inq[v]=true,!q.empty()&&dis[v]<dis[q.front()])q.push_front(v);
                    else q.push_back(v);
}

int n, m, a[kM], b[kM], t[kM];
int ans[kN], fa[kN];
std::vector<int>g[kN];
lt *h[kN];

inline int anc(int u) { return fa[u]?fa[u]=anc(fa[u]):u; }
void Union(int u,int v) { if((u=anc(u))!=(v=anc(v)))fa[u]=v; }

void dp(int u) {
    for (int i=g[u].size()-1;i>=0;i--) {
        dp(g[u][i]);
        h[u] = Merge(h[u],h[g[u][i]]);
        Union(u,g[u][i]);
    }
    while (!Empty(h[u]) && anc(Top(h[u]).u)==anc(Top(h[u]).v)) Pop(h[u]);
    if (Empty(h[u])) ans[u] = -1;
    else ans[u] = Top(h[u]).w-dis[u];
}

int main() {
    int i;
    r(n), r(m);
    Rep (i,1,m) r(a[i]), r(b[i]), r(t[i]), Add(a[i],b[i],t[i]);
    spfa();
    Rep (i,1,n) g[pre[i]].push_back(i), h[i] = null;
    Rep (i,1,m) {
        if (pre[a[i]]==b[i] || pre[b[i]]==a[i]) continue;
        Insert(h[a[i]],(ltnode){dis[a[i]]+dis[b[i]]+t[i],a[i],b[i]});
        Insert(h[b[i]],(ltnode){dis[a[i]]+dis[b[i]]+t[i],a[i],b[i]});
    }
    dp(1);
    Rep (i,2,n) printf("%d\n", ans[i]);
    END: getchar(), getchar();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值