描述
输入
第一行: 两个空格分开的数, N和M
第2…M+1行: 三个空格分开的数a_i, b_i,和t_i
输出
第1…N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间.如果这样的路经不存在,输出-1.
样例输入
4 5
1 2 2
1 3 2
3 4 4
3 2 1
2 4 3
样例输出
3
3
6
Analysis
只要题目中说了源点到每个点最短路径是唯一的 那往往都和最短路树有关系
我们就先跑一遍Dijkstra建出最短路树
(事实上也并不是真正意义的建树,其实就是明确每个点的父亲)
然后由于这道题我们不可以走原最短路的最后一条
我们只能通过非树边进行更新,得到“次短路”
这样,我们要找的节点i的去掉最短路最后一条边后的最短路,就是
d
i
s
[
u
]
+
w
[
u
,
v
]
+
d
i
s
[
v
]
−
d
i
s
[
i
]
.
dis[u]+w[u,v]+dis[v]−dis[i] .
dis[u]+w[u,v]+dis[v]−dis[i].其中,dis[] 代表节点到根节点的最短路径,w[u,v] 代表u 到v 的路径长度;
接着我们发现对于每一条非树边其
d
i
s
[
u
]
+
w
[
u
,
v
]
+
d
i
s
[
v
]
dis[u]+w[u,v]+dis[v]
dis[u]+w[u,v]+dis[v]是一个定值,因而我们可以预先算出来,然后从小到大排序,枚举加入更新
一个很显然的事实,如果一个点在之前被更新过了,那么下一次枚举的非树边就不可以再更新它(因为我们是按照从小到大排的序)
我们用并查集加以维护即可
Code
/*created by xly*/
#include<bits/stdc++.h>
#define in read()
#define re register
#define ll long long
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<1)+(res<<3)+(ch^48);
ch=getchar();
}
return f==1?res:-res;
}
const int N=1e5+10,M=4e5+10;
bool vis[N];
int n,m,tfa[N],d[N],fa[N],ans[N];
int nxt[M],head[N],ecnt=0,cnt=0;
struct node{int u,v,w;}e[M],tree[M];
inline bool cmp(const node &a,const node &b){return a.w<b.w;}
inline void add(int x,int y,int z){
nxt[++ecnt]=head[x];head[x]=ecnt;
e[ecnt].u=x;e[ecnt].v=y;e[ecnt].w=z;
}
inline int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}
inline void dij(){
priority_queue<pair<int,int> > q;
memset(d,127/3,sizeof(d));
q.push(make_pair(0,1));d[1]=0;
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u]) continue;
vis[u]=1;
for(re int i=head[u];i;i=nxt[i]){
int v=e[i].v;
if(d[v]>d[u]+e[i].w){
d[v]=d[u]+e[i].w;
tfa[v]=u;
q.push(make_pair(-d[v],v));
}
}
}
}
int main(){
n=in;m=in;
for(re int i=1;i<=m;++i){
int x,y,z;
x=in;y=in;z=in;
add(x,y,z);add(y,x,z);
}
dij();
for(re int i=1;i<=2*m;i+=2){//寻找非树边
int u=e[i].u,v=e[i].v;
if(tfa[u]==v||tfa[v]==u) continue;
tree[++cnt].w=d[u]+e[i].w+d[v];
tree[cnt].u=u;
tree[cnt].v=v;
}
sort(tree+1,tree+cnt+1,cmp);
memset(ans,-1,sizeof(ans));
for(re int i=1;i<=n;++i) fa[i]=i;
for(re int i=1;i<=cnt;++i){
int u=getfa(tree[i].u),v=getfa(tree[i].v);
while(u!=v){//把这条非树边能够更新的都更新
if(d[u]<d[v]) swap(u,v);
ans[u]=tree[i].w-d[u];
fa[u]=tfa[u];
u=getfa(u);
}
}
for(re int i=2;i<=n;++i) printf("%d\n",ans[i]);
return 0;
}