[Wannafly挑战赛2D-Delete]最短路

[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

题意为:给定一个DAG,多次询问任意删掉一个点之后,S到T的最短路。

首先,这是一个DAG,因此S到T的最短路是可以沿拓扑序DP直接求出的,因此我们不花半点力气,就得到了一个O(nq)的算法。

 

现在我们考虑删点对于一个DAG的影响,即之前通过拓扑序求出的拓扑图其中一个点不能通过。

先特判S到T不连通的情况,直接输出-1即可。

现在我们保证了S到T有一条可行路径。考虑拓扑图之外的边对其影响,它们会提供一种可行的方式跨过被删点连向之后的节点,形成最短路的总代价为  dist(S,u)+dist(v,T)+dis(u,v)   ,而这一条路会对在拓扑图中u,v两点之间所有点产生相同的影响。即u,v之间所有点形成的最短路,都能用这一条边形成的路径代替(不保证最短,只保证可行)。

于是问题变成了:一堆边会改变拓扑序中连续的一段区间[l,r]的点的代价,求任意某点的最优代价。区间修改+单点查询,线段树维护最小值即可。

时间复杂度为  O(nlgn)  。

 

#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 second

using 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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值