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],對於邊和,則id[x]<id[u]<id[y];(拓撲序爲1的點到其餘點的最短距離能O(n)dp出來)
所以,如果有一條邊(x,y),x,y點拓撲序是a,b。對於每個拓撲序在間的點,都有一條路徑跨過了點。所以,如果刪除了u節點,可以用s->x + v(x,y) +y->t這條路徑代替。
將區間的值更新爲不大於該路徑的長度,這樣,當刪除一個節點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;
}