• 图论——线段树优化建图


    链接:CF786B Legacy

    题意:

    有一个n个节点的有向有权图
    有三种类型的边:
    1.从u到v的一条边权为w的边
    2.从u到区间[l,r]任意一个点都有一条边权为w的边
    3.从区间[l,r]中任意一个点到v点都有一条边权为w的边
    求从点1到其他所有点的最短路。若不可达,则输出-1

    题解:

    第一种类型的边很容易建立,但对于第二、三种类型的边,如果采用区间每一个点都连边的方式,连边数量太多不可接受。
    考虑到是区间问题,我们可以采用线段树优化建图的方式解决。
    建立两颗线段树,第一颗存储的是入边的线段树,也就是单点到区间这样的边,另外一颗就是出边的线段树。
    用第二种类型的边举例:
    假设有这样的一条边:2——>[1,3],边权为w,n = 4
    则我们需要将出边树的叶子节点[2,2]向入边树的节点[1,2]与[3,3]连边,边权为w
    而线段树内部点之间的边权定为0,则建图大概就是这样的:

    第三种类型的边就是出边树若干个节点向入边树的一个叶子节点连边即可
    还需要考虑一点的是:因为左右两边叶子节点都是代表原图中的某个节点,因此我们需要将叶子节点一一对应的连接起来,边权为0

    /*
    线段树优化建图
    */
    #include <bits/stdc++.h>
    using namespace std;
    
    #define mp make_pair
    #define pb push_back
    typedef long long ll;
    typedef pair<ll,int> pii;
    const int maxn = 1e5+10;
    const ll inf = (1ll<<55);
    vector<pii> G[maxn*20];
    int n,q,s;
    int base;
    int tree[maxn*20];
    void add(int u,int v,int w){
        G[u].pb(mp(w,v));
    }
    
    void build(int l,int r,int p){
        if(l == r){
            tree[l] = p;
            return ;
        }
        add(p,p<<1,0),add(p,p<<1|1,0);
        add(base+(p<<1),base+p,0),add(base+(p<<1|1),base+p,0);
        int m = (l+r)>>1;
        build(l,m,p<<1);
        build(m+1,r,p<<1|1);
    }
    
    void update(int op,int v,int w,int L,int R,int l,int r,int p){
        // if(L>r || R<L) return ;
        if(L<=l && r<=R){
            if(op == 2) add(v+base,p,w);
            else if(op == 3) add(p+base,v,w);
            return ;
        }
        int m = (l+r)>>1;
        if(L<=m) update(op,v,w,L,R,l,m,p<<1);
        if(R>m) update(op,v,w,L,R,m+1,r,p<<1|1);
    }
    
    ll dis[maxn*20];
    bool vis[maxn*20];
    
    void dij(int s){
        memset(dis,0x3f,sizeof(dis));
        dis[s] = 0;
    
        priority_queue<pii,vector<pii>,greater<pii> > q;
        q.push(mp(0,s));
    
        while(!q.empty()){
            int u=q.top().second;
            q.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for (auto i : G[u]){
                int v=i.second;
                ll w=i.first;
                if(dis[v] > dis[u] + w){
                    dis[v]=dis[u]+w;
                    q.push(mp(dis[v],v));
                }
            }
        }
    }
    
    int main(){
        cin>>n>>q>>s;
        base = 1000000;
        build(1,n,1);
        for (int i=1;i<=n;i++){
            add(tree[i],tree[i]+base,0);
            add(tree[i]+base,tree[i],0);
        }
        
        for (int i=1;i<=q;i++){
            int op;
            scanf("%d",&op);
            
            if(op == 1){
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                if(u == v) continue;
                add(tree[u],tree[v],w);
            }
            else {
                int v,l,r,w;
                scanf("%d%d%d%d",&v,&l,&r,&w);
                update(op,tree[v],w,l,r,1,n,1);
            }
        }
    
        dij(tree[s]);
        for (int i=1;i<=n;i++){
            printf("%lld ",dis[tree[i]] >= inf ? -1 : dis[tree[i]]);
        }
        return 0;
    }
    
    你将不再是道具,而是成为人如其名的人
  • 相关阅读:
    Linux常用命令(二)
    C#异步调用
    Ajax运用总结
    SQL Server2005 FOR XML PATH 语句的应用
    SQL Server 2005 处理交叉表
    如何控制并发和控制死锁(内含pb的处理要点)
    WPF 中的换行符
    C#委托的异步调用
    pivot 附近有语法错误,您可能需要将当前数据库的兼容级别设置为更高的值。
    应用系统BUG分布小结
  • 原文地址:https://www.cnblogs.com/wsl-lld/p/14803945.html
Copyright © 2020-2023  润新知