题目描述
给出一个n个点m条边的无向图,n个点的编号从1~n,定义源点为1。定义最短路树如下:从源点1经过边集T到任意一点i有且仅有一条路径,且这条路径是整个图1到i的最短路径,边集T构成最短路树。
给出最短路树,求对于除了源点1外的每个点i,求最短路,要求不经过给出的最短路树上的1到i的路径的最后一条边。
题解
设表示第个点到根的路径和
一个点在不向上走一步的情况下,应该是在其子树内找到一个点,走到那然后通过一条非树边走出这棵子树,然后走到根更优
即第个点的答案应为
(在子树内,在子树外)
考虑最小化,即每条非树边造成的影响
对于一条非树边,它所能影响到的点在以及的路径上且不包括
所以树剖,以树剖序建线段树,然后区间更新最小值即可
效率
(有更好的方法,利用并查集,效率为)
#include <cstdio>
#include <algorithm>
#include <cstring>
#define Ls k<<1
#define Rs Ls|1
#define F 0x3f3f3f3f
#define I inline
#define E register
using namespace std;
const int N=4005;struct O{int u,v,w;}p[100005];
int n,m,c,t,head[N],V[N*2],nex[N*2],W[N*2],d[N],fa[N];
int dep[N],top[N],sz[N],son[N],id[N],tt,a[N*4],dfn[N],s[N];
I void add(E int u,E int v,E int w){
V[++t]=v;nex[t]=head[u];head[u]=t;W[t]=w;
}
I void dfs1(E int x,E int fat,E int deep){
fa[x]=fat;dep[x]=deep;sz[x]=1;
for (E int i=head[x];i;i=nex[i]){
if (V[i]==fat) continue;
d[V[i]]=d[x]+W[i];
dfs1(V[i],x,deep+1);
sz[x]+=sz[V[i]];
if (sz[V[i]]>sz[son[x]])
son[x]=V[i];
}
}
I void dfs2(E int x,E int tp){
top[x]=tp;id[x]=++tt;dfn[tt]=x;
if (son[x]) dfs2(son[x],tp);
for (E int i=head[x];i;i=nex[i])
if (V[i]^son[x] && V[i]^fa[x])
dfs2(V[i],V[i]);
}
I void query(E int k,E int l,E int r,E int w){
if (l==r){s[dfn[l]]=min(w,a[k]);return;}
E int mid=l+r>>1;
query(Ls,l,mid,min(a[k],w));
query(Rs,mid+1,r,min(a[k],w));
}
I void update(E int k,E int l,E int r,E int L,E int R,E int w){
if (L<=l && r<=R){a[k]=min(a[k],w);return;}
E int mid=l+r>>1;
if (mid>=L) update(Ls,l,mid,L,R,w);
if (mid<R) update(Rs,mid+1,r,L,R,w);
}
I void update_chain(E int x,E int y,E int w){
while(top[x]^top[y]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,1,n,id[top[x]],id[x],w);x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
update(1,1,n,id[x]+1,id[y],w);
}
int main(){
scanf("%d%d",&n,&m);memset(a,F,sizeof a);
for (E int u,v,w,ty,i=1;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&w,&ty);
if (ty) add(u,v,w),add(v,u,w);
else p[++c]=(O){u,v,w};
}
dfs1(1,0,1);dfs2(1,1);
for (E int i=1;i<=c;i++)
update_chain(p[i].u,p[i].v,d[p[i].u]+d[p[i].v]+p[i].w);
query(1,1,n,F);
for (E int i=2;i<=n;i++)
if (s[i]==F) puts("-1");
else printf("%d ",s[i]-d[i]);
return 0;
}