【bzoj 3073】Journeys(线段树优化建图)

传送门biu~
线段树的每个节点代表一个区间,建两棵线段树。
出线段树每个点向父节点连边0,表示如果能从这个区间出发也就可以从父区间出发。入线段树每个点向子节点连边0,表示如果能到达这个区间也就可以到达子区间。
入线段树每个点向出线段树的平行结点连边0,表示如果能到达这个区间也可以从这个区间出发。
在线段树上跑最短路,到叶子节点的路径长即为最短路径。

#include<bits/stdc++.h>
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]<dis[to[i]]){
                dis[to[i]]=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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zP1nG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值