传送门biu~
线段树的每个节点代表一个区间,建两棵线段树。
出线段树每个点向父节点连边0,表示如果能从这个区间出发也就可以从父区间出发。入线段树每个点向子节点连边0,表示如果能到达这个区间也就可以到达子区间。
入线段树每个点向出线段树的平行结点连边0,表示如果能到达这个区间也可以从这个区间出发。
在线段树上跑最短路,到叶子节点的路径长即为最短路径。
#include
using namespace std;
int n,m,p,cnt;
int head[4000005],nex[30000005],to[30000005],v[30000005],tp;
int dis[4000005],pos[4000005];
bool in[4000005];
void spfa(int st){
memset(dis,127,sizeof(dis));
dis[st]=0;
queue<int>q;q.push(st);
while(!q.empty()){
int x=q.front();q.pop();in[x]=0;
for(int i=head[x];i;i=nex[i]){
if(dis[x]+v[i]if(!in[to[i]]){
in[to[i]]=1;
q.push(to[i]);
}
}
}
}
}
inline void add(int x,int y,int z){
nex[++tp]=head[x];
head[x]=tp;
to[tp]=y;
v[tp]=z;
}
struct Node{
Node *ch[2];
int L,R,num;
Node(int l=0,int r=0){L=l;R=r;num=cnt;ch[0]=ch[1]=NULL;}
}*root;
void init(Node *&o,int l,int r){
++cnt;o=new Node(l,r);
if(l==r) {pos[l]=cnt;return;}
int mid=l+r>>1;
init(o->ch[0],l,mid); init(o->ch[1],mid+1,r);
}
void build(Node *&o){
add(o->num+cnt,o->num,0);
if(o->L==o->R) return;
for(int i=0;i<=1;++i){
add(o->ch[i]->num,o->num,0);
add(o->num+cnt,o->ch[i]->num+cnt,0);
build(o->ch[i]);
}
}
void Link(Node *&o,int l,int r,int ord,int opt){
if(o->L==l && o->R==r){
if(!opt) add(o->num,ord,1);
else add(ord,o->num+cnt,0);
return;
}
int mid=o->L+o->R>>1;
if(r<=mid) Link(o->ch[0],l,r,ord,opt);
else if(l>mid) Link(o->ch[1],l,r,ord,opt);
else Link(o->ch[0],l,mid,ord,opt),Link(o->ch[1],mid+1,r,ord,opt);
}
int main(){
scanf("%d%d%d",&n,&m,&p);
init(root,1,n);
build(root);
for(int i=1;i<=m;++i)
for(int j=0;j<=1;++j){
int x,y;
scanf("%d%d",&x,&y);
Link(root,x,y,i+2*cnt,j);
Link(root,x,y,i+2*cnt+m,j^1);
}
spfa(pos[p]);
for(int i=1;i<=n;++i) printf("%d\n",dis[pos[i]]);
return 0;
}