1. 程式人生 > 實用技巧 >Wannafly挑戰賽2D Delete 刪點最短路(拓撲序+最短路+線段樹)

Wannafly挑戰賽2D Delete 刪點最短路(拓撲序+最短路+線段樹)

題目

(Delete)https://ac.nowcoder.com/acm/problem/14293

題目描述

給定一張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

輸入

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

思路

因為是DGA,那麼就可以拓撲排序,每個點都有一個拓撲序,那麼x點的拓撲序Top[x],假如x點被刪除,那麼經過x的最短路,一定是經過了一條邊u-v
\(Top[u]<Top[x]&&Top[v]>Top[x]\)並且\(d1[u]!=inf&&d2[v]!=inf\)的最短路。我們列舉所有的邊,並且把區間修改\([Top[u]+1, Top[v]-1]\)

的最小值修改為\(d1[u]+W_{u-v}+d2[v]\)
對於一個點區間查詢就可以了。這裡我們一次查詢把所有的標記下轉到葉子節點就可以了。

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define pLL pair<long long, long long>
#define LL long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)

using namespace std;

char buf[1<<20],*p1=buf,*p2=buf;
inline LL read(){
    char c=getchar();LL x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

struct Edge {
    int from, to;
    LL w;
    int nxt;
} e[2000005], e2[2000005];
int head[100005], head2[100005], cut=0, cut2=0;

void Addedge(int x, int y, LL w){
    e[++cut]={x, y, w, head[x]}; head[x]=cut;
}

void Addedgef(int x, int y, LL w){
    e2[++cut2]={x, y, w, head2[x]}; head2[x]=cut2;
}

priority_queue<pLL> q;
int vis[100005];
void DP(int s, LL d[], int head[], Edge e[]){
    q.push({d[s]=0, s});
    while(!q.empty()){
        pLL now=q.top(); q.pop();
        int u=now.second;
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u]; i; i=e[i].nxt){
            int to=e[i].to; LL w=e[i].w;
            if(d[to]>d[u]+w){
                d[to]=d[u]+w;
                q.push({-d[to], to});
            }
        }
    }
}

int d[100005];
int id[100005], fid[100005], pos=0;
queue<int> qq;
void Top(int s, int n){
    for(int i=1; i<=n; i++){
        if(d[i]==0){
            qq.push(i);
        }
    }
    while(!qq.empty()){
        int now=qq.front(); qq.pop();
        id[now]=++pos; fid[pos]=now;
        for(int i=head[now]; i; i=e[i].nxt){
            d[e[i].to]--;
            if(!d[e[i].to]){
                qq.push(e[i].to);
            }
        }
    }
}

LL ans[100005];
LL d1[100005], d2[100005];
LL inf;
struct Tree{
    LL mi[100005*4];
    void init(){
        memset(mi, 0x3f, sizeof(mi));
        inf=mi[0];
    }
    void up_data(int rt, int l, int r, int L, int R, LL w){
        if(l==L&&r==R){
            mi[rt]=min(mi[rt], w);
            return ;
        }
        int mid=l+r>>1;
        if(R<=mid) up_data(rt<<1, l, mid, L, R, w);
        else if(L>mid) up_data(rt<<1|1, mid+1, r, L, R, w);
        else up_data(rt<<1, l, mid, L, mid, w), up_data(rt<<1|1, mid+1, r, mid+1, R, w);
    }
    void qurey(int rt, int l, int r, LL w){
        if(l==r){
            if(d1[fid[l]]==inf||d2[fid[l]]==inf) ans[fid[l]]=w;
            else ans[fid[l]]=mi[rt];
            return ;
        }
        else{
            int mid=l+r>>1;
            mi[rt<<1]=min(mi[rt<<1], mi[rt]);
            mi[rt<<1|1]=min(mi[rt<<1|1], mi[rt]);
            qurey(rt<<1, l, mid, w); qurey(rt<<1|1, mid+1, r, w);
        }
    }
}T;

int main() {
    int n=read(), m=read(), s=read(), t=read(), x, y;
    for(int i=1; i<=m; i++){
        x=read(), y=read();
        LL w=read();
        d[y]++;
        Addedge(x, y, w);
        Addedgef(y, x, w);
    }
    memset(d1, 0x3f, sizeof(d1));
    memset(vis, 0, sizeof(vis));
    DP(s, d1, head, e);
    memset(d2, 0x3f, sizeof(d2));
    memset(vis, 0, sizeof(vis));
    DP(t, d2, head2, e2);
    Top(s, n);
    T.init();
    for(int i=1; i<=m; i++){
        int x=e[i].from, y=e[i].to;
        if(id[x]!=id[y]-1&&d1[x]!=inf&&d2[y]!=inf){
            T.up_data(1, 1, n, id[x]+1, id[y]-1, d1[x]+d2[y]+e[i].w);
        }
    }
    T.qurey(1, 1, n, d1[t]);
    int q=read();
    while(q--){
        int x=read();
        printf("%lld\n", (ans[x]==inf)?-1:ans[x]);
    }

    return 0;
}
/*
5 6 1 5
1 3 4
1 2 1
2 3 1
2 4 1
3 5 1
4 3 1
*/