- 有一张 (n) 个点 (m) 条边的无向图,边长均为 (a)。在原图中所有满足最短路长为 (2a) 的点对之间连一条边长为 (b) 的边。
- 求给定点 (st) 到所有点的距离。
- (1le n,mle10^5),(1le a,ble10^3)
原图上的 BFS
根据 (b) 与 (a) 的大小关系,最短路径可能有三种:全是 (a);若干 (b) 和一个 (a);全是 (b)。
先在原图中 BFS 一遍,求出 (st) 到所有点的最小边数 (d_i)。
也就是说,对于全是 (a) 的情况,最短路径长度就是 (d_i imes a)。
根据最短路的性质,最短路上第 (k) 个点和第 (k+2) 个点之间必然无边(否则就不是最短路了),因此我们必然可以把这条最短路径压缩,但可能会多出一个 (a)。
也就是说,对于若干个 (b) 和一个 (a) 的情况,最短路径长度就是 (lfloorfrac{d_i}2 floor imes b+(d_ioperatorname{mod}2) imes a)。
只走 (b) 的BFS
然后考虑全是 (b) 的情况,我们重新 BFS 一遍,要求每次只能走 (b)。
有点类似于求三元环,但我们要求的实际上是三元非环。先枚举一遍当前点 (x) 连向的点 (y) 打标记,再枚举一遍当前点 (x) 连向的点 (y) 并枚举 (y) 连向的点 (z),如果 (z) 没被打上标记,那么就可以从 (x) 向 (z) 转移。
但我们并没有进行求三元环预先给边定向的操作,并且也无法进行这个操作,因此这样的复杂度是不正确的。
不过考虑 BFS 的性质,就是每个点 (z) 只会被转移一次。我们不妨在转移后就把这条边给删掉。
注意,具体实现中需要维护两个边集,其中枚举 (x) 连向的点是用原边集,枚举 (y) 连向的点是用转移边集,而我们删边仅是在转移边集中删去。
因此,第二重枚举中的一条边被枚举到时,除非它构成了一个三元环,否则就会被删去。而三元环的个数是 (O(msqrt m)) 的,故复杂度正确。
代码:(O(msqrt m))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
#define INF (int)1e9
using namespace std;
int n,m,st,a,b,d[N+5],q[N+5],vis[N+5],ans[N+5];
struct Graph
{
int ee,lnk[N+5];struct edge {int to,pre,nxt;}e[2*N+5];I Graph() {ee=1;}
I int operator [] (CI x) Cn {return lnk[x];}I edge operator () (CI x) Cn {return e[x];}
I void Add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]].pre=ee,e[lnk[x]=ee].to=y;}
I void Del(CI id) {(e[id].pre?e[e[id].pre].nxt:lnk[e[id^1].to])=e[id].nxt,e[e[id].nxt].pre=e[id].pre;}//删边
}G,K;
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
');}
}using namespace FastIO;
int main()
{
RI i,j,x,y;for(read(n,m,st,a,b),i=1;i<=m;++i) read(x,y),G.Add(x,y),G.Add(y,x),K.Add(x,y),K.Add(y,x);
RI k,H,T;for(i=1;i<=n;++i) d[i]=-1;d[q[H=T=1]=st]=0;
W(H<=T) for(i=G[k=q[H++]];i;i=G(i).nxt) !~d[G(i).to]&&(d[q[++T]=G(i).to]=d[k]+1);//原图上的BFS
for(i=1;i<=n;++i) ans[i]=~d[i]?min(d[i]*a,(d[i]&1)*a+(d[i]>>1)*b):INF;
for(i=1;i<=n;++i) d[i]=-1;d[q[H=T=1]=st]=0;W(H<=T) {for(i=G[k=q[H++]];i;i=G(i).nxt) vis[G(i).to]=k;//给当前点连向的点打标记
for(i=G[k];i;i=G(i).nxt) for(j=K[G(i).to];j;j=K(j).nxt) vis[K(j).to]^k&&(K.Del(j),!~d[K(j).to]&&(d[q[++T]=K(j).to]=d[k]+1));}//二重枚举,不构成三元环即可删边
for(i=1;i<=n;++i) ~d[i]&&(ans[i]=min(ans[i],d[i]*b));for(i=1;i<=n;++i) writeln(ans[i]);return clear(),0;
}