Wannafly挑戰賽2D Delete 刪點最短路(拓撲序+最短路+線段樹)
阿新 • • 發佈:2020-09-12
題目
(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]\)
對於一個點區間查詢就可以了。這裡我們一次查詢把所有的標記下轉到葉子節點就可以了。
#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 */