Never again making up stories in dismay

QOJ 20303

初看这道题,感觉非常恐怖啊。

感觉不管从起点还是终点入手我们的复杂读度都是不能接受的,而 \(S\) 的优美性质给了我们机会。因为只从 \(S\) 开始搜索,我们是只用搜极少次的。

因此我们考虑转化问题,不妨转化为 \(u \to S \to v\),且贪心的,我们肯定希望到达 \(S\) 的时间尽可能早。所以,问题转化为求出 \(u \to S\) 的最快时间与求出在一定时间下, \(S \to v\) 的最快时间。

先考虑求出第一部分。不难想到的是按时间从小到大进行加边,然后维护每一个点的可达,但是这样子太慢了!能不能优化?我们考虑加边的方式的问题在于同一边会被多次处理,因此我们需要避免这种情况。因此我们需要让每一次加边都有用,先考虑什么时候的加边肯定有用?不难想到的是建一个反的图,则 从 \(S\) 出发的边很有用,所以,从小到大,去扩展,感觉就很对。

形式化的,我们对于每一个点 \(u\) 的边,关于 \(T\) 进行排序,然后维护每一个点的 \(Limit\),表示到达 \(u\) 点的最迟时间。然后我们对于 \(S\) ,一步步加上边,每一次加边后,进行扩展,修改 \(Limit\),然后就是对的了。

对于另一部分,实际上我们的处理是相似的,只不过前一个是一步步拉高上限,后面一个是一步步降低下限而已,不再赘述。

Code

因为是做的搬的题目,所以没有写函数式……

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 2e5 + 10;
const int INF = 1e9 + 7;
int n,m,S;
vector<pair<int,int> > G[N],E[N];
int a[N],limit[N],f[N];
struct node{
	int v,id,d;
};
vector<node> ques;
int ans[N];
bool cmp(node x,node y){
	return x.d < y.d;
}
int main(){
	IOS;
	cin >> n >> m >> S;
	for(int i=1;i<=m;i++){
		int u,v,T;
		cin >> u >> v >> T;
		G[u].push_back({T,v});
		E[v].push_back({-T,u});
	}
	for(int i=1;i<=n;i++){
		sort(E[i].begin(),E[i].end());
		for(int j=0;j<E[i].size();j++)
			E[i][j].fi = -E[i][j].fi;
		sort(G[i].begin(),G[i].end());
	}
	memset(a,-1,sizeof a);
	memset(limit,-1,sizeof limit);
	a[S] = 0;
	while(!E[S].empty()){
		queue<int> q;
		q.push(E[S].back().se);
		limit[S] = E[S].back().fi;
		limit[E[S].back().se] = max(limit[E[S].back().se],limit[S]);
		if(a[E[S].back().se] == -1) a[E[S].back().se] = limit[S];
		E[S].pop_back();
		int T = limit[S];
		while(!q.empty()){
			int u = q.front();
			q.pop();
			while(!E[u].empty() && E[u].back().fi <= limit[u]){
				int v = E[u].back().se;
				int w = E[u].back().fi;
				E[u].pop_back();
				if(limit[v] < w){
					limit[v] = w;
					q.push(v);
					if(a[v] == -1)
						a[v] = T;
				}
			}
		}
	}
	int Q;
	cin >> Q;
	fill(f+1,f+1+n,INF);
	for(int i=1;i<=Q;i++){
		int u,v;
		cin >> u >> v;
		if(a[u] == -1){
			ans[i] = -1;
			continue;
		}
		if(v == S){
			ans[i] = a[u];
			continue;
		}
		ques.push_back({v,i,a[u]});
	}
	sort(ques.begin(),ques.end(),cmp);
	// f[S] = 0;
	while(!G[S].empty()){
		queue<int> q;
		int Sv = G[S].back().se;
		int Sw = G[S].back().fi;
		q.push(Sv);
		while(!ques.empty() && ques.back().d > Sw){
			int v = ques.back().v;
			int id = ques.back().id;
			if(f[v] == INF) ans[id] = -1;
			else ans[id] = f[v];
			ques.pop_back();
		}
		f[Sv] = min(f[Sv],Sw);
		G[S].pop_back();
		while(!q.empty()){
			int u = q.front();
			q.pop();
			while(!G[u].empty() && G[u].back().fi >= f[u]){
				int v = G[u].back().se;
				int w = G[u].back().fi;
				G[u].pop_back();
				if(v == S) continue;
				if(f[v] >= w){
					f[v] = w;
					q.push(v);
				}
			}
		}
	}
	while(!ques.empty()){
		int v = ques.back().v;
		int id = ques.back().id;
		if(f[v] == INF) ans[id] = -1;
		else ans[id] = f[v];
		ques.pop_back();
	}
	for(int i=1;i<=Q;i++)
		cout << ans[i] << "\n";
	return 0;
}
posted @ 2026-09-29 19:34  WinterXorSnow  阅读(8)  评论(0)    收藏  举报