题目:BZOJ1576.
题目大意:给定一张
n
n
n个点
m
m
m条边的无向图,询问对于
1
1
1到
n
n
n的每个点
i
i
i,最短的一条从
1
1
1到
i
i
i的路径,使得这条路径的最后一条边与
1
1
1到
i
i
i的最短路的最后一条边不同.保证对于任意
i
i
i,
1
1
1到
i
i
i的最短路径是唯一的.
1
≤
n
≤
1
0
5
,
1
≤
m
≤
2
∗
1
0
5
1\leq n\leq 10^5,1\leq m\leq 2*10^5
1≤n≤105,1≤m≤2∗105.
很容易想到我们可以先跑出一棵最短路树,这个可以直接用最短路算法实现.
然后对于每一条非树边 ( x , y , v ) (x,y,v) (x,y,v),考虑它可以对 ( x , y ) (x,y) (x,y)链上除LCA外的点 k k k,都提供一条路径 1 → x → y → k 1\rightarrow x\rightarrow y\rightarrow k 1→x→y→k,容易发现其路径长度为 d i s [ x ] + d i s [ y ] + v − d i s [ k ] dis[x]+dis[y]+v-dis[k] dis[x]+dis[y]+v−dis[k],其中 d i s [ i ] dis[i] dis[i]表示点 1 1 1到点 i i i的最短路径长度.
容易发现 d i s [ x ] + d i s [ y ] + v dis[x]+dis[y]+v dis[x]+dis[y]+v相对于一条确定的边 ( x , y , v ) (x,y,v) (x,y,v)来说是常数, − d i s [ k ] -dis[k] −dis[k]相对于枚举的点 k k k也是常数,而我们要求每个点 k k k上的 d i s [ x ] + d i s [ y ] + v − d i s [ k ] dis[x]+dis[y]+v-dis[k] dis[x]+dis[y]+v−dis[k]最小.
这个时候就可以把所有非树边按照 d i s [ x ] + d i s [ y ] + v dis[x]+dis[y]+v dis[x]+dis[y]+v从小到大排序,并依次枚举所有边,每次把链 ( x , y ) (x,y) (x,y)上除LCA外的所有点的答案覆盖为 d i s [ x ] + d i s [ y ] + v dis[x]+dis[y]+v dis[x]+dis[y]+v(减掉 d i s [ k ] dis[k] dis[k]可以在最后处理),并把这些点从树上删去,这可以用一个经典的并查集实现.
具体来说,我们定义并查集中一个点 x x x的祖先 a n c x anc_{x} ancx表示当前 x x x的祖先中第一个未被删去的点,然后可以用一种类似于树链剖分跳链的方式实现这个覆盖并删除的过程.
时间复杂度 O ( ( n + m ) log m ) O((n+m)\log m) O((n+m)logm).
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100000,M=200000,INF=(1<<30)-1;
int n,m;
struct side{
int y,next,v;
}e[M*2+9];
int lin[N+9],cs;
void Ins(int x,int y,int v){e[++cs].y=y;e[cs].v=v;e[cs].next=lin[x];lin[x]=cs;}
void Ins2(int x,int y,int v){Ins(x,y,v);Ins(y,x,v);}
void into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i){
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
Ins2(x,y,v);
}
}
struct state{
int x,v;
state(int X=0,int V=0){x=X;v=V;}
bool operator > (const state &p)const{return v>p.v;}
};
priority_queue<state,vector<state>,greater<state> >q;
int dis[N+9],vis[N+9];
void Dijkstra(){
for (int i=1;i<=n;++i) dis[i]=INF,vis[i]=0;
q.push(state(1,dis[1]=0));
for (;!q.empty();){
int t=q.top().x;q.pop();
if (vis[t]) continue;
vis[t]=1;
for (int i=lin[t];i;i=e[i].next)
if (dis[t]+e[i].v<dis[e[i].y])
q.push(state(e[i].y,dis[e[i].y]=dis[t]+e[i].v));
}
}
struct side2{
int x,y,v,id;
side2(int X=0,int Y=0,int V=0,int Id=0){x=X;y=Y;v=V;id=Id;}
}ord[M+9];
int co;
bool cmp(const side2 &a,const side2 &b){return a.v<b.v||a.v==b.v&&a.id<b.id;}
int fa[N+9],dep[N+9];
void Dfs_fa(int k,int fat){
fa[k]=fat;
dep[k]=dep[fat]+1;
for (int i=lin[k];i;i=e[i].next){
if (e[i].y==fat) continue;
if (dis[k]+e[i].v==dis[e[i].y]) Dfs_fa(e[i].y,k);
else if (k<e[i].y) ord[++co]=side2(k,e[i].y,dis[k]+dis[e[i].y]+e[i].v,co);
}
}
struct union_find_set{
int fa[N+9];
int &operator [] (const int &p){return fa[p];}
void Build(int l,int r){for (int i=l;i<=r;++i) fa[i]=i;}
int Query_fa(int k){return k^fa[k]?fa[k]=Query_fa(fa[k]):k;}
}uni;
int ans[N+9];
void Get_ans(){
uni.Build(1,n);
sort(ord+1,ord+co+1,cmp);
for (int i=1;i<=co;++i){
int x=uni.Query_fa(ord[i].x),y=uni.Query_fa(ord[i].y),v=ord[i].v;
for (;x^y;x=uni.Query_fa(x)){
if (dep[x]<dep[y]) swap(x,y);
ans[x]=v-dis[x];
uni[x]=fa[x];
}
}
}
void work(){
Dijkstra();
Dfs_fa(1,0);
Get_ans();
}
void outo(){
for (int i=2;i<=n;++i)
printf("%d\n",ans[i]?ans[i]:-1);
}
int main(){
into();
work();
outo();
return 0;
}