codeforces 787D (线段树建图+dij)

题意: 现在有n个点,有一个起始点,你要求出从起始点到其他点的最短路,那么这就是一个很简单的最短路问题,但是麻烦的地方在建图,普通的建图这里肯定是不行的。因为这里有三种操作,第一 从u到v 加一条边,权值 为w 第二 从u到 l,r 的点都可以,权值为w ,第三 从  l,r 到u点,权值为w 。那么对于这种区间,我们可以用线段树建图的方法,

这里方法是:

建立两颗线段树

A线段树每个节点和其父亲节点之间有一条边,边权为0

B线段树每个节点和其两个儿子节点之间有一条边,边权为0

B线段树的每个叶节点和A线段树的每个叶节点之间有一条边,边权为0

对于每一个op==1 的操作,我们建一条从A线段树叶节点到B线段树叶节点的边,边权为w

对于每一个op==2的操作,我们建一条从A线段树叶节点到B线段树区间节点的边,边权为w 因为B线段树是从上向下建,那么就相当于u节点可以到B区间节点的区间内的每一个节点。

对于每一个op==3 的操作,我们建一条从A线段树区间节点到B线段树叶子节点的边,边权为w 因为A线段树我们是从下向上建,那么就相当于A区间节点内的每一个点都可以到达B点的叶子节点。

最后跑一个最短路就可以了。

 

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2000005;
const int maxm = 10000005;
typedef long long LL;
const LL inf = 0x3f3f3f3f3f3f3f3f;
int ls[maxn],rs[maxn],L[maxn],R[maxn],num;
int ver[maxm],he[maxn],ne[maxm],tot;
LL cost[maxm];
void add( int x,int y,LL w ){
    ver[++tot] = y;
    ne[tot] = he[x];
    he[x] = tot;
    cost[tot] = w;
}
int buildup( int l,int r ){
    if( l == r ){
        return L[l] = R[l] = l;
    }
    int id = ++num;
    int mid = l+r>>1;
    L[id] = l;R[id] = r;
    ls[id] = buildup( l,mid );
    rs[id] = buildup( mid+1,r );
    add( ls[id],id,0 );
    add( rs[id],id,0 );
    return id;
}
int builddown( int l,int r ){
    if( l == r ){
        return L[l] = R[l] = l;
    }
    int id = ++num;
    L[id] = l;R[id] = r;
    int mid = l+r>>1;
    ls[id] = builddown( l,mid );
    rs[id] = builddown( mid+1,r );
    add( id,ls[id],0 );
    add( id,rs[id],0 );
    return id;
}
void update1( int l,int r,int v,LL w,int x ){
    if( R[x] < l || L[x] > r ) return;
    if( l <= L[x] && r >= R[x] ){
        add( x,v,w );
        return;
    }
    update1( l,r,v,w,ls[x] );
    update1( l,r,v,w,rs[x] );
}
void update2( int l,int r,int u,LL w,int x ){
    if( R[x] < l || L[x] > r ) return;
    if( l <= L[x] && r >= R[x] ){
        add( u,x,w );
        return;
    }
    update2( l,r,u,w,ls[x] );
    update2( l,r,u,w,rs[x] );
}
priority_queue< pair<LL,int>,vector< pair<LL,int> >,greater< pair<LL,int> > > que2;
LL dist[maxn];
int vis[maxn];
void dijkstra( int S,int n ){
    while( que2.size() ) que2.pop();
    memset( dist,0x3f,sizeof(dist) );
    dist[S] = 0;
    que2.push( make_pair( 0,S ) );
    while( que2.size() ){
        int x = que2.top().second;
        que2.pop();
        //if( x == T ) break;
        if( vis[x] ) continue;
        vis[x] = 1;
        for( int cure = he[x];cure;cure = ne[cure] ){
            int y = ver[cure];
            if( vis[y] ) continue;
            if( dist[y] > dist[x] + cost[cure] ){
                dist[y] = dist[x] + cost[cure];
                que2.push( make_pair( dist[y],y ) );
            }
        }
    }
}
int main(){
    int n,q,s;
    scanf("%d%d%d",&n,&q,&s);
    num = n;
    int root1 = buildup( 1,n ),root2 = builddown(1,n);
    for( int i = 1;i <= q;i++ ){
        int op,u,v,w,l,r;
        scanf("%d",&op);
        if( op == 1 ){
            scanf("%d%d%d",&u,&v,&w);
            update2( v,v,u,w,root2 );
        }else if( op == 2 ){
            scanf("%d%d%d%d",&u,&l,&r,&w);
            update2( l,r,u,w,root2 );
        }else{
            scanf("%d%d%d%d",&v,&l,&r,&w);
            update1( l,r,v,w,root1 );
        }
    }
    dijkstra(s,num);
    for( int i = 1;i <= n;i++ ){
        if( dist[i] != inf )printf("%I64d ",dist[i]);
        else printf("-1 ");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值