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)(x,u)(u,y)(u,y),則id[x]<id[u]<id[y];(拓撲序爲1的點到其餘點的最短距離能O(n)dp出來)
所以,如果有一條邊(x,y),x,y點拓撲序是a,b。對於每個拓撲序在(a,b)(a,b)間的點uiu_i,都有一條路徑跨過了uiu_i點。所以,如果刪除了u節點,可以用s->x + v(x,y) +y->t這條路徑代替。
將區間[a+1,b1][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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章