[Wannafly挑战赛2D-Delete]最短路
题目描述
给定一张 n 个点,m 条边的带权有向无环图,同时给定起点 S 和终点 T ,一共有 q 个询问,每次询问删掉某个点和所有与它相连的边之后 S 到 T 的最短路,询问之间互相独立(即删除操作在询问结束之后会立即撤销),如果删了那个点后不存在 S 到 T 的最短路,则输出 −1 。点的编号为 1 到 n 。
输入格式
第一行四个正整数表示 n,m,S,T ,意义如题所述。
接下来 m 行每行三个正整数 x,y,z ,表示有一条 x 到 y 的有向边,权值为 z 。
接下来一行一个正整数 Q 表示询问次数。
最后 Q 行每行一个正整数 k 表示这次询问要删除点 k 。
输出格式
输出 Q 行,每行一个整数表示答案。
样例一
input
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
output
23
15
23
14
限制与约定
对于 100% 的数据,1≤S,T,x,y,k≤n≤10^5 ;1≤Q≤10^5 ;1≤m≤2×10^5 ;0≤z≤10^9
Solution
题意为:给定一个,多次询问任意删掉一个点之后,S到T的最短路。
首先,这是一个,因此S到T的最短路是可以沿拓扑序DP直接求出的,因此我们不花半点力气,就得到了一个的算法。
现在我们考虑删点对于一个的影响,即之前通过拓扑序求出的拓扑图其中一个点不能通过。
先特判S到T不连通的情况,直接输出-1即可。
现在我们保证了S到T有一条可行路径。考虑拓扑图之外的边对其影响,它们会提供一种可行的方式跨过被删点连向之后的节点,形成最短路的总代价为 ,而这一条路会对在拓扑图中u,v两点之间所有点产生相同的影响。即u,v之间所有点形成的最短路,都能用这一条边形成的路径代替(不保证最短,只保证可行)。
于是问题变成了:一堆边会改变拓扑序中连续的一段区间[l,r]的点的代价,求任意某点的最优代价。区间修改+单点查询,线段树维护最小值即可。
时间复杂度为 。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std;template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=1e9+7;
const ll INF=1ll<<60;
const int MAXN=1e5+500;
/*--------------------------------------------------------------------*/
struct enode{int v; ll c; };
vector<enode> e1[MAXN],e2[MAXN];
int d1[MAXN],d2[MAXN],dfn[MAXN];
ll f1[MAXN],f2[MAXN];
queue<int> que;
inline int read()
{int f=1,x=0; char c=getchar();while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }return x*f;
}
inline char readc()
{char c=getchar();while (!isalnum(c)) c=getchar();return c;
}
struct Segment_Tree
{struct treenode{int l,r; ll s; } tree[MAXN<<2];void build(int x,int l, int r){tree[x].s=INF;if ((tree[x].l=l)==(tree[x].r=r)) return;int mid=(tree[x].l+tree[x].r)>>1;build(x<<1,l,mid);build(x<<1|1,mid+1,r);}void change(int x,int L,int R,ll val){if (L<=tree[x].l&&tree[x].r<=R) { tree[x].s=min(tree[x].s,val); return; }int mid=(tree[x].l+tree[x].r)>>1;if (L<=mid) change(x<<1,L,R,val);if (mid<R) change(x<<1|1,L,R,val);}ll query(int x,int y){if (tree[x].l==tree[x].r) return tree[x].s;int mid=(tree[x].l+tree[x].r)>>1;if (y<=mid) return min(tree[x].s,query(x<<1,y));else return min(tree[x].s,query(x<<1|1,y));}
} segment;
int main()
{//freopen("shortest.in","r",stdin);//freopen("shortest.out","w",stdout);int n=read(),m=read(),S=read(),T=read();for (int i=1;i<=m;i++){int u=read(),v=read(),c=read();e1[u].push_back((enode){v,c});e2[v].push_back((enode){u,c});d1[v]++; d2[u]++;}int Dfn=0;for (int i=1;i<=n;i++) f1[i]=INF; f1[S]=0;for (int i=1;i<=n;i++) if (d1[i]==0) que.push(i); while (!que.empty()){int q=que.front();dfn[q]=++Dfn;que.pop();for (int i=0;i<e1[q].size();i++){int to=e1[q][i].v,c=e1[q][i].c;d1[to]--;f1[to]=min(f1[to],f1[q]+c);if (d1[to]==0) que.push(to);}}que.push(T);for (int i=1;i<=n;i++) f2[i]=INF; f2[T]=0;for (int i=1;i<=n;i++) if (d2[i]==0) que.push(i);while (!que.empty()){int q=que.front();que.pop();for (int i=0;i<e2[q].size();i++){int to=e2[q][i].v,c=e2[q][i].c;d2[to]--;f2[to]=min(f2[to],f2[q]+c);if (d2[to]==0) que.push(to);}}segment.build(1,1,n);for (int i=1;i<=n;i++) for (int j=0;j<e1[i].size();j++)if (f1[i]!=INF&&f2[e1[i][j].v]!=INF&&dfn[i]+1<dfn[e1[i][j].v]) segment.change(1,dfn[i]+1,dfn[e1[i][j].v]-1,f1[i]+f2[e1[i][j].v]+e1[i][j].c);int Case=read();while (Case--){int x=read();if (f1[x]==INF||f2[x]==INF) printf("%lld\n",f1[T]);else {ll q=segment.query(1,dfn[x]);printf("%lld\n",q==INF?-1:q);}}return 0;
}