题目大意:求从1到每个点不经过最短路的最后一条边的所有方案中的最短路
首先构出最短路径树(有所有在最短路上的边构成的树)
这样所有的答案路径都是从起点开始沿着树上的边走到一个点然后走一个不是树上的边,然后再在树上走
这样对于一条非树边(u,v)他能更新的答案就是对于所有的在(u,v)树上路径上的点(不包括LCA)x,ans[x]=min(ans[x],dis[u]+dis[v]+w-dis[x])
显然暴力更新是不可取的,所以我们可以把非树边按照dis[u]+dis[v]+w排序,这样第一个更新到的就是答案,接着可以用并查集保证每个点只被更新一次
卡SPFA差评!!!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 200010
using namespace std;
int A[N],B[N],C[N];
int to[N<<1],nxt[N<<1],num[N<<1],w[N<<1],pre[N],cnt;
void ae(int ff,int tt,int ww,int nn)
{
cnt++;
to[cnt]=tt;
nxt[cnt]=pre[ff];
w[cnt]=ww;
num[cnt]=nn;
pre[ff]=cnt;
}
void ae(int ff,int tt)
{
cnt++;
to[cnt]=tt;
nxt[cnt]=pre[ff];
pre[ff]=cnt;
}
int d[N],bef[N];
bool vis[N];
struct qqq{int x,w;};
bool operator <(const qqq &x,const qqq &y){return x.w>y.w;}
priority_queue<qqq>q;
void dij()
{
memset(d,0x3f,sizeof(d));
d[1]=0;
q.push((qqq){1,0});
int i,j,x;
while(!q.empty())
{
x=q.top().x;q.pop();
if(vis[x]) continue;
vis[x]=true;
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if(vis[j]||d[j]<=d[x]+w[i]) continue;
bef[j]=num[i];
d[j]=d[x]+w[i];
q.push((qqq){j,d[j]});
}
}
}
bool spc[N];
struct ppp{int u,v,w;}b[N];
bool cmp(ppp x,ppp y){return x.w<y.w;}
int FA[N];
void build(int x)
{
int i,j;
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if(j==FA[x]) continue;
FA[j]=x;
build(j);
}
}
int fa[N];
int find(int x)
{
if(fa[x]==x) return x;
fa[x]=find(fa[x]);
return fa[x];
}
int ans[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int i,j,x,y,z;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&A[i],&B[i],&C[i]);
ae(A[i],B[i],C[i],i);ae(B[i],A[i],C[i],i);
}
dij();
cnt=0;memset(pre,0,sizeof(pre));
for(i=2;i<=n;i++)
{
x=bef[i];
ae(A[x],B[x]);
ae(B[x],A[x]);
spc[x]=true;
}
build(1);
for(i=1;i<=n;i++)
fa[i]=i;
int cn=0;
for(i=1;i<=m;i++)
if(!spc[i])
{
cn++;
b[cn]=(ppp){A[i],B[i],d[A[i]]+d[B[i]]+C[i]};
}
sort(b+1,b+cn+1,cmp);
for(i=1;i<=cn;i++)
{
x=find(b[i].u);y=find(b[i].v);
while(x!=y)
{
if(d[x]<d[y]) swap(x,y);
ans[x]=b[i].w-d[x];
fa[x]=FA[x];
x=find(x);
}
}
for(i=2;i<=n;i++)
{
if(ans[i]) printf("%d\n",ans[i]);
else puts("-1");
}
}