zoukankan      html  css  js  c++  java
  • [DAG上最短路+线段树]Delete

    题目描述

    链接:https://ac.nowcoder.com/acm/contest/17/D?&headNav=www&headNav=acm
    来源:牛客网

    给定一张n个点,m条边的带权有向无环图,同时给定起点S和终点T,一共有q个询问,每次询问删掉某个点和所有与它相连的边之后S到T的最短路,询问之间互相独立(即删除操作在询问结束之后会立即撤销),如果删了那个点后不存在S到T的最短路,则输出-1。

    题解

    单源最短路一般采用的是dijkstra算法,堆优化的时间复杂度是(O(VlgV+E))

    但是这道题是DAG,就不需要从dijkstra上考虑问题。从有向无环图的性质上思考这道题。

    DAG:有向无环图

    在DAG上求最短路最快(好)的方法是,先拓扑排序,再根据这个拓扑排序的序列枚举每条边,松弛更新后面的点。

    可以这样做是因为当前点肯定由与它的前驱,并且拓扑序在其前面的点更新。这样就有了最优子结构,可以由类似动态规划的方法做出来。

    这道题求出拓扑序之后,可以发现:

    一个点删除后,如果还存在路径,应用于拓扑序上:肯定存在一条边跨过了这个点。

    如图,如果4被删除,肯定由35构成新路径。

    如果5被删除,肯定由46构成新路径。

    删除后如果同时有两条边跨过了这个点:如下图的5,存在边37和边27跨过这个点,那就选择最优的路径。

    经过这条路径的次短路怎么求:s到这个点的最短路+边权+t到这个点的最短路(反图)

    现在怎样有一个高效的方法快速得到跨过这个点的最短的路径?

    线段树

    从图中会发现,抽象出来,这个就是线段树的区间修改更新最小值,点查询的操作。

    每条边((u,v))((u+1,v-1))的区间有贡献,这个贡献就是经过((u,v))的次短路,可以通过正路径和反路径得出。

    代码

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 1e5 + 10;
    int n;
    struct edge{
        int ed;
        ll val;
    };
    struct graph{
        int s,t;
        int in[maxn],topIndex[maxn];
        ll dis[maxn];
        vector<edge>G[maxn];
        void addEdge(int u,int v,int w){
            in[v]++;
            G[u].push_back(edge{v,w});
        }
        int arr[maxn],aIndex = 0;
        void topSort(){
            queue<int>que;
            for(int i=1;i<=n;++i){
                if(in[i] == 0){
                    que.push(i);
                    topIndex[i] = aIndex;
                    arr[aIndex++] = i;
                }
            }
            while(!que.empty()){
                int u = que.front();
                que.pop();
                for(edge tmp:G[u]){
                    if(--in[tmp.ed] == 0){
                        que.push(tmp.ed);
                        topIndex[tmp.ed] = aIndex;
                        arr[aIndex++] = tmp.ed;
                    }
                }
            }
        }
        void shortPath(){
            memset(dis,-1,sizeof(dis));
            dis[s] = 0;
            for(int i = 0;i < aIndex;++i){
                if(dis[arr[i]] == -1){
                    continue;
                }
                for(edge tmp:G[arr[i]]){
                    if(dis[tmp.ed] == -1 || dis[arr[i]] + tmp.val < dis[tmp.ed]){
                        dis[tmp.ed] = dis[arr[i]] + tmp.val;
                    }
                }
            }
        }
    }forwGraph,revGraph;
    class SegmentTree{
    public:
        long long tag[maxn << 2];
        void build(int l, int r, int rt){
            tag[rt] = 1e18;
            if (l == r) return;
            int m = (l + r) >> 1;
            build(l, m, rt << 1);
            build(m + 1, r, rt << 1 | 1);
        }
        void update(int L, int R, long long val, int l, int r, int rt){
            if (L <= l && r <= R){
                tag[rt] = min(tag[rt], val);
                return;
            }
            int m = (l + r) >> 1;
            if (L <= m) update(L, R, val, l, m, rt << 1);
            if (m <  R) update(L, R, val, m + 1, r, rt << 1 | 1);
        }
        void query(int x, int l, int r, int rt, long long &ans){
            ans = min(ans, tag[rt]);
            if (l == r) return;
            int m = (l + r) >> 1;
            if (x <= m) query(x, l, m, rt << 1, ans);
            else query(x, m + 1, r, rt << 1 | 1, ans);
        }
    }st;
    void buildTree(){
        for(int i=1;i<=n;++i){
            for(edge tmp : forwGraph.G[i]){
                ll disu = forwGraph.dis[i];
                ll disv = revGraph.dis[tmp.ed];
                int u = forwGraph.topIndex[i];
                int v = forwGraph.topIndex[tmp.ed];
                if(disu != -1 && disv != -1 && u + 1 < v){
                    st.update(u + 1,v - 1,disu + disv + tmp.val,1,n,1);
                }
            }
        }
    }
    int main(){
        int m,S,T;
        scanf("%d%d%d%d",&n,&m,&S,&T);
        for(int i=1,u,v,w;i <= m;++i){
            scanf("%d%d%d",&u,&v,&w);
            forwGraph.addEdge(u,v,w);
            revGraph.addEdge(v,u,w);
        }
        forwGraph.s = S;
        revGraph.s = T;
        forwGraph.topSort();
        forwGraph.shortPath();
        revGraph.topSort();
        revGraph.shortPath();
        int Q;
        scanf("%d",&Q);
        st.build(1,n,1);
        buildTree();
        while(Q--){
            int x;
            scanf("%d",&x);
            ll ans = 1e18;
            if(forwGraph.dis[T] == -1){
                puts("-1");
                continue;
            }
            if(forwGraph.dis[x] == -1 || revGraph.dis[x] == -1){
                printf("%lld
    ",forwGraph.dis[T]);
                continue;
            }
            st.query(forwGraph.topIndex[x],1,n,1,ans);
            if(ans == 1e18){
                printf("-1
    ");
            }else{
                printf("%lld
    ",ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    python---模块与包
    python---迭代器与生成器
    python---装饰器
    Python---函数
    Python---文件操作
    Python---数据类型
    浅谈UBUNTU
    java 键盘输入多种方法
    动态规划解最长公共子序列问题
    线段树
  • 原文地址:https://www.cnblogs.com/smallocean/p/12862594.html
Copyright © 2011-2022 走看看