给出一个n个点m条边的无向图,n个点的编号从1~n,定义源点为1。定义最短路树如下:从源点1经过边集T到任意一点i有且仅有一条路径,且这条路径是整个图1到i的最短路径,边集T构成最短路树。
给出最短路树,求对于除了源点1外的每个点i,求最短路,要求不经过给出的最短路树上的1到i的路径的最后一条边。
建出最短路径树,考虑对于每一条非树边 (u,v) ,则 u,v 及以上的点、 lca(u,v) 以下的点都会由这条非树边多出一条路径。求出 disi 为 1~i 的最短路径,那么对于每个 u,v 及以上的点、 lca 以下的点,新增的这条路径的长度为 disu+disv+len(u,v)−disx ,又因为对于每一个 x,disx 是不变的,现在我们就是求出能用来更新x的最小的非树边。
理解了图的性质就可以用并查集,令每一条非树边的权值为 disu+disv+len(u,v)−disx ,排好序后一旦有一个点被更新了,就不会被其他的值更新,用并查集维护更新的链顶就行了。
#include
using namespace std;
const int N=4e3+5,M=1e5+5,G=M<<1;
int n,m,tpt;
int tot,head[N];
int h[N],fa[N],dis[N],f[N],bel[N];
struct data{
int x,y,l,len;
inline bool operator < (const data &rhs)const{
return lenstruct point{
int to,val,nxt;
}V[G];
template<class T>inline void read(T &res){
static char ch;T flag=1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
while((ch=getchar())>='0'&&ch<='9')res=ch-48+res*10;res*=flag;
}
inline void addedge(int x,int y,int z){
V[++tot].nxt=head[x],head[x]=tot,V[tot].to=y,V[tot].val=z;
}
inline void dfs(int x,int f,int dep){
h[x]=dep,fa[x]=f;
for(register int y,i=head[x];i;i=V[i].nxt)
if(y=V[i].to,y!=f)
dis[y]=dis[x]+V[i].val,dfs(y,x,dep+1);
}
inline int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
int main(){
read(n),read(m);
for(register int a,b,l,t,i=1;i<=m;++i){
read(a),read(b),read(l),read(t);
if(t==1)addedge(a,b,l),addedge(b,a,l);
else E[++tpt].x=a,E[tpt].y=b,E[tpt].l=l;
}
dfs(1,0,1);
for(register int i=1;i<=tpt;++i)E[i].len=E[i].l+dis[E[i].x]+dis[E[i].y];
sort(E+1,E+tpt+1);
for(register int i=1;i<=n;++i)f[i]=i;
for(register int i=1;i<=tpt;++i){
int u=E[i].x,v=E[i].y,ff=find(u),fs=find(v),tail=0,last=0;
while(ff!=fs){
if(h[ff]if(!bel[u]){
bel[u]=i;
if(tail)f[tail]=u;
}else if(tail)f[tail]=ff;
tail=ff,u=fa[tail],ff=find(u);
}
}
if(bel[2])printf("%d",E[bel[2]].len-dis[2]);
else printf("-1");
for(register int i=3;i<=n;++i)
if(bel[i])printf(" %d",E[bel[i]].len-dis[i]);
else printf(" -1");
return 0;
}