zoukankan      html  css  js  c++  java
  • topo序 最短路 线段树

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

    输入描述:
    第一行四个正整数表示n,m,S,T,意义如题所述;
    接下来m行每行三个正整数x[i],y[i],z[i],表示有一条x[i]到y[i]的有向边,权值为z[i];
    第m+1行一个正整数q表示询问次数;
    接下来q行每行一个正整数a[i]表示这次询问要删除点a[i]。
    n,q <= 10^5
    m <= 2*10^5
    z[i] <= 10^9
    输出描述:
    q行每行一个数输出答案,如果删了这个点后不存在S到T的最短路,输出-1
    示例1
    输入

    6 7 1 5
    1 2 2
    2 3 4
    3 4 3
    4 5 5
    3 5 9
    1 6 10
    6 5 13
    4
    3
    4
    2
    6
    

    输出

    23
    15
    23
    14
    

    首先,DAG图必定能拓扑排序,x点的topo序为id[x],对于边((x,u))((u,y)),则id[x]<id[u]<id[y];(拓扑序为1的点到其余点的最短距离能O(n)dp出来)
    所以,如果有一条边(x,y),x,y点拓扑序是a,b。对于每个拓扑序在((a,b))间的点(u_i),都有一条路径跨过了(u_i)点。所以,如果删除了u节点,可以用s->x + v(x,y) +y->t这条路径代替。
    将区间([a+1,b-1])的值更新为不大于该路径的长度,这样,当删除一个节点z时,查询每个包含z的区间,如果有的话,这必定是一条从s到t跨过z的路径。

    #include<bits/stdc++.h>
    #include<algorithm>
    #include<iostream>
    #include<queue>
    using namespace std;
    template<class T>inline bool read(T &x){
        x=0;register char c=getchar();register bool f=0;
        while(!isdigit(c)){if(c==EOF)return false;f^=c=='-',c=getchar();}
        while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
        if(f)x=-x;
        return true;
    }
    template<class T>inline void print(T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)print(x/10);
        putchar('0'+x%10);
    }
    template<class T>inline void print(T x,char c){print(x),putchar(c);}
    template<class T>inline bool read(T&a,T&b){return read(a)&&read(b);}
    template<class T>inline bool read(T&a,T&b,T&c){return read(a)&&read(b)&&read(c);}
    typedef long long ll;
    const int inf=0x3f3f3f3f,MAXN=1e5+8,mod=998244353;
    const ll INF=1ll<<60;
    #define Init(arr,val) memset(arr,val,sizeof(arr))
    #define lowbit(x) (x&(-x))
    #define lson (i<<1)
    #define rson (i<<1|1)
    #define mid ((l+r)>>1)
    int n,m,s,t;
    struct E{int y;ll v,nt;}e1[MAXN<<1],e2[MAXN<<1];//正向边、反向边
    int head1[MAXN],cnt1,head2[MAXN],cnt2;
    inline void add(E e[],int head[],int &cnt,int x,int y,ll v){
        e[++cnt].y=y;e[cnt].v=v;
        e[cnt].nt=head[x];head[x]=cnt;
    }
    int rudu[MAXN],id[MAXN],kth;//id[x]:x节点拓扑序
    void topo(){
        queue<int>que;
        for(int i=1;i<=n;++i)if(!rudu[i])que.push(i);
        int x,y;
        while(!que.empty()){
            x=que.front();que.pop();
            id[x]=++kth;
            for(int i=head1[x];i;i=e1[i].nt){
                y=e1[i].y;
                if(--rudu[y]==0)que.push(y);
            }
        }
    }
    struct H{int y;ll v;bool operator<(const H&o)const{return v>o.v;}};
    priority_queue<H>heap;
    ll dis1[MAXN],dis2[MAXN];
    bool vis[MAXN];
    void dijkstra(ll dis[],int head[],E e[],int s){
        Init(vis,0);
        dis[s]=0;
        heap.push((H){s,0});
        int x,y;ll v;
        while(!heap.empty()){
            x=heap.top().y;heap.pop();
            if(vis[x])continue;vis[x]=1;
            for(int i=head[x];i;i=e[i].nt){
                y=e[i].y,v=e[i].v;
                if(dis[y]>dis[x]+v){
                    dis[y]=dis[x]+v;
                    if(!vis[y])heap.push((H){y,dis[y]});
                }
            }
        }
    }
    ll seg[MAXN<<2];
    void change(int x,int y,ll v,int i=1,int l=1,int r=n){
        if(x<=l&&r<=y){seg[i]=min(seg[i],v);return;}
        if(x<=mid)change(x,y,v,lson,l,mid);
        if(y>mid)change(x,y,v,rson,mid+1,r);
        //没有up,因为区间不能向上更新
    }
    ll query(int x,int i=1,int l=1,int r=n){
        if(l==r)return seg[i];
        ll res=seg[i];//每个覆盖了点x的区间都要求最小值。
        if(x<=mid)return min(res,query(x,lson,l,mid));
        else return min(res,query(x,rson,mid+1,r));
    }
    int main(){
        read(n,m),read(s,t);
        Init(head1,0);Init(head2,0);
        int x,y;ll v;
        for(int i=0;i<m;++i){
            read(x,y);read(v);
            add(e1,head1,cnt1,x,y,v);
            add(e2,head2,cnt2,y,x,v);
            ++rudu[y];
        }
        for(int i=0;i<MAXN;++i)dis1[i]=dis2[i]=INF;
        for(int i=0;i<MAXN<<2;++i)seg[i]=INF;
        dijkstra(dis1,head1,e1,s);
        dijkstra(dis2,head2,e2,t);
        topo();
        for(x=1;x<=n;++x)if(dis1[x]!=inf){//如果s能去x节点
            for(int i=head1[x];i;i=e1[i].nt)if(dis2[e1[i].y]!=inf){//y能到t
                y=e1[i].y;
                if(id[x]+1<=id[y]-1)//区间里有其他点就更新
                change(id[x]+1,id[y]-1,dis1[x]+e1[i].v+dis2[y]);
            }
        }
        int q;read(q);
        while(q--){
            read(x);
            //如果s->t的路径与x无关
            if(dis1[x]==INF||dis2[x]==INF)printf("%lld
    ",dis1[t]==INF?-1:dis1[t]);
            else{
                v=query(id[x]);
                if(v>=INF)v=-1;
                printf("%lld
    ",v);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Android开发之漫漫长途 XVII——动画(续)
    Android开发之漫漫长途 XVII——动画
    Android开发之漫漫长途 XVI——ListView与RecyclerView项目实战
    Android开发之漫漫长途 XV——RecyclerView
    Android开发之漫漫长途 XIV——ListView
    .net core 发布IIS502错误
    读取加密秘钥文件失败
    MVC项目发布IIS访问不了
    a标签的onclick和href事件的区别
    文件上传
  • 原文地址:https://www.cnblogs.com/foursmonth/p/14155922.html
Copyright © 2011-2022 走看看