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\n",dis1[t]==INF?-1:dis1[t]);
        else{
            v=query(id[x]);
            if(v>=INF)v=-1;
            printf("%lld\n",v);
        }
    }
    return 0;
}
posted @ 2020-12-18 17:11  肆之月  阅读(108)  评论(0编辑  收藏  举报