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

来源:互联网 发布:光学透镜设计软件 编辑:程序博客网 时间:2024/06/05 13:28

题意

NM边,N1头牛从1沿最短路径走到对应的2...NN1个点。询问避开牛经过的最后一条边到达相应点的最短路径长度。输出这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 0
原创粉丝点击