[USACO09JAN]安全出行Safe Travel

Gremlins最近在农场上泛滥,它们经常会阻止牛们从农庄(牛棚_1)走到别的牛棚(牛_i的目的 地是牛棚_i).每一个gremlin只认识牛_i并且知道牛_i一般走到牛棚_i的最短路经.所以它 们在牛_i到牛棚_i之前的最后一条牛路上等牛_i. 当然,牛不愿意遇到Gremlins,所以准备找 一条稍微不同的路经从牛棚_1走到牛棚_i.所以,请你为每一头牛_i找出避免gremlin_i的最 短路经的长度.

和以往一样, 农场上的M (2 <= M <= 200,000)条双向牛路编号为1..M并且能让所有牛到 达它们的目的地, N(3 <= N <= 100,000)个编号为1..N的牛棚.牛路i连接牛棚a_i (1 <= a_i <= N)和b_i (1 <= b_i <= N)并且需要时间t_i (1 <=t_i <= 1,000)通过. 没有两条牛路连接同样的牛棚,所有牛路满足a_i!=b_i.在所有数据中,牛_i使用的牛棚_1到牛 棚_i的最短路经是唯一的.

输入输出格式

输入格式:

  • Line 1: Two space-separated integers: N and M

  • Lines 2..M+1: Three space-separated integers: a_i, b_i, and t_i

输出格式:

  • Lines 1..N-1: Line i contains the smallest time required to travel from pasture_1 to pasture_i+1 while avoiding the final cowpath of the shortest path from pasture_1 to pasture_i+1. If no such path exists from pasture_1 to pasture_i+1, output -1 alone on the line.

输入输出样例

输入样例#1: 复制
4 5 
1 2 2 
1 3 2 
3 4 4 
3 2 1 
2 4 3 
输出样例#1: 复制
3 
3 
6 

思路:首先dijkstra跑一遍最短路(卡spfa),单源最短路构成一颗生成树,要求到源点1的次短路,显然必须要通过不在树上的边,这样我们只要求min{w(u,v)+d(u)+d(v)-d(f)}。
从上面式子看出,经过点f的环中w(u,v)+d(u)+d(v)最小时即是次短路,那么只要将不在树上的边与树上的边构成的环的权值从小到大排序,就能贪心得到答案,并用并查集维护那些点被更新过。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
#define N 100010
using namespace std;
int n,m,cnt,u,v,w,head[N],cnt2,dis[N],num,f[N],fa[N],set[N];//f数组为i到1的次短路
bool vis[N];
struct node{
  int next,to,dis;
}e[N*10],g[N*10];
void add(int u,int v,int w)
{
  e[++cnt].next=head[u],e[cnt].to=v,e[cnt].dis=w,head[u]=cnt;
}
void add2(int u,int v,int w)
{
  g[++cnt2].next=u,g[cnt2].to=v,g[cnt2].dis=w;
}
void dij(int u)
{
  priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
  memset(dis,0x3f3f3f3f,sizeof(dis));
  q.push(make_pair(0,u)),dis[u]=0;
  while(!q.empty())
    {
      int u=q.top().second;q.pop();
      if(vis[u])continue;vis[u]=1;
      for(int i=head[u];i;i=e[i].next)
    {
      int v=e[i].to;
      if(dis[v]>dis[u]+e[i].dis)
        {
          dis[v]=dis[u]+e[i].dis;
          fa[v]=u;
          q.push(make_pair(dis[v],v));
        }
    }
    }
}
bool cmp(const node&a,const node&b)
{
  return a.dis<b.dis;
}
int find(int x)
{
  return x==set[x]?x:set[x]=find(set[x]);
}
void kk(node x)
{
  int u=x.next,v=x.to,w=x.dis;
  while(find(u)!=find(v))
    {
      num++;
      if(dis[find(u)]<dis[find(v)])swap(u,v);
      f[find(u)]=min(f[find(u)],w-dis[find(u)]);//更新当前结点
      u=set[find(u)]=fa[find(u)];
    }
}
int main()
{
  memset(f,0x3f3f3f3f,sizeof(f));
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)set[i]=i;
  for(int i=1;i<=m;i++)scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
  dij(1);
  for(int i=1;i<=n;i++)
    for(int j=head[i];j;j=e[j].next)
      {
    int u=i,v=e[j].to;
    if(u!=fa[v]&&v!=fa[u])//边不在最短路树上
      add2(u,v,dis[u]+dis[v]+e[j].dis);//加上环权值和
      }
  sort(g+1,g+1+cnt2,cmp);//按环权排序
  for(int i=1;i<=cnt2&&num<=n-1;i++)//枚举非树上结点,每次把环上没被赋值过的点赋上值,并查集记录树上哪些点被更新过
    kk(g[i]);
  for(int i=2;i<=n;i++)
    {
      if(f[i]<0x3f3f3f3f)printf("%d
",f[i]);
      else puts("-1");
    }
  return 0;
}
原文地址:https://www.cnblogs.com/lxykk/p/7757494.html