先建立最短路径树(即跑dij每一个点向更新他的点连边),考虑一个点的答案路径一定要走过且仅走过一条非树边,枚举非树边(x,y),对于一个点k,如果它在x~lca上(y~lca的路径上同理),那么答案可以更改为s[y]+len(x,y)+s[x]-s[k],前三个不受k的影响,因此越小越好,即将每一条边按照这个排序后依次加入,用并查集来维护即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define L (k<<1) 5 #define R (L+1) 6 #define mid (l+r>>1) 7 int E,n,m,x,y,z,k,d[N],fa[N],f[N],head[N],ans[N]; 8 struct ji{ 9 int x,y; 10 bool operator < (const ji &a)const{ 11 return y>a.y; 12 } 13 }; 14 struct ji2{ 15 int fr,nex,to,len; 16 bool operator < (const ji2 &a)const{ 17 return len+d[fr]+d[to]<a.len+d[a.fr]+d[a.to]; 18 } 19 }edge[N<<2]; 20 priority_queue<ji>q; 21 void add(int x,int y,int z){ 22 edge[E]=ji2{x,head[x],y,z}; 23 head[x]=E++; 24 } 25 int find(int k){ 26 if (k==f[k])return k; 27 return f[k]=find(f[k]); 28 } 29 int main(){ 30 scanf("%d%d",&n,&m); 31 memset(head,-1,sizeof(head)); 32 for(int i=1;i<=m;i++){ 33 scanf("%d%d%d",&x,&y,&z); 34 add(x,y,z); 35 add(y,x,z); 36 } 37 memset(d,0x3f,sizeof(d)); 38 q.push(ji{d[1]=0,1}); 39 while (!q.empty()){ 40 x=q.top().y; 41 q.pop(); 42 for(int j=head[x];j!=-1;j=edge[j].nex) 43 if (d[edge[j].to]>d[x]+edge[j].len){ 44 fa[edge[j].to]=x; 45 q.push(ji{d[edge[j].to]=d[x]+edge[j].len,edge[j].to}); 46 } 47 } 48 sort(edge,edge+E); 49 memset(ans,-1,sizeof(ans)); 50 for(int i=1;i<=n;i++)f[i]=i; 51 for(int i=0;i<E;i++) 52 if ((fa[edge[i].to]!=edge[i].fr)&&(fa[edge[i].fr]!=edge[i].to)){ 53 x=find(edge[i].fr); 54 y=find(edge[i].to); 55 z=edge[i].len+d[edge[i].fr]+d[edge[i].to]; 56 while (x!=y){ 57 if (d[x]<d[y])swap(x,y); 58 ans[x]=z-d[x]; 59 f[find(x)]=find(fa[x]); 60 x=find(x); 61 } 62 } 63 for(int i=2;i<=n;i++)printf("%d\n",ans[i]); 64 }