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
",dis1[t]==INF?-1:dis1[t]);
else{
v=query(id[x]);
if(v>=INF)v=-1;
printf("%lld
",v);
}
}
return 0;
}