题解:CF2128F Strict Triangle

感觉有点小牛啊。

题意:给出一个图,每个边有限制区间 \([l_i,r_i]\),现在再给出一个点 \(k\),问是否存在一种分配边权的方式,使得最短路不经过 \(k\)\(n,m\le 2\times 10^5\)

做法:

首先容易发现肯定是让我们要的那条路径全都是最小值,其他的都取最大值最好。

直接找最短路径很麻烦,但是我们只需要找到一条路径 \(P\),使得路径 \(P\) 优于经过 \(k\) 的最短路 \(Q\) 就可以了。

容易观察到,路径 \(P,Q\) 相交的是一段前缀,那么就要求 \(\forall u,v,\operatorname{dis}_l(u,v)<\operatorname{dis}_r(u,k)+\operatorname{dis}_r(v,k)\) 才合法,这里 \(\operatorname{dis}_{l/r}\) 是指边权为全取 \(l/r\) 的图中的最短路。

考虑这个东西该咋维护很好,这里卡住了我非常久。我们这么考虑确定 \(P\),从前往后进行确定,同时维护一个限制,也就是要求 \(\operatorname{dis}_l(u,v)-\operatorname{dis}_r(u,k)<\operatorname{dis}_r(v,k)\) 都要满足。我们对于一条路径上维护最大的 \(f_u = \operatorname{dis}_l(x,u)-\operatorname{dis}_r(x,k)\)\(f\) 是上面那个不等式左侧的限制,考虑转移到 \(v\),则令 \(f_v\gets \max(f_v,f_u+l)\)。记得对于 \(f_u\) 还要对 \(-\operatorname{dis}_r(u,k)\)\(\max\)

考虑按什么顺序转移,注意到我们希望这个限制越小越好,所以优先取出 \(f_u\) 最小的转移。

什么时候不合法呢?即 \(f_n\ge \operatorname{dis}_r(n,k)\) 就不合法。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5 + 5;
int n, m, k;
struct Edge {
	int to, l, r;
};
vector<Edge> e[maxn];
int dis[maxn], dp[maxn];
struct node {
	int p, val;
	friend bool operator<(node x, node y) {
		return x.val > y.val;
	}
} ;
priority_queue<node> q;
int vis[maxn];
void dijkstra1(int s) {
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	dis[s] = 0; q.push(node{s, 0});
	while(!q.empty()) {
		int u = q.top().p; q.pop();
		if(vis[u])
			continue;
		vis[u] = 1;
		for (int i = 0; i < e[u].size(); i++) {
			Edge v = e[u][i];
			if(dis[v.to] > dis[u] + v.r) {
				dis[v.to] = dis[u] + v.r;
				q.push(node{v.to, dis[v.to]});
			}
		}
	}
}
bool dijkstra2(int s) {
	memset(dp, 0x3f, sizeof(dp));
	memset(vis, 0, sizeof(vis));
	dp[s] = -dis[s], q.push(node{s, dp[s]});
	while(!q.empty()) {
		int u = q.top().p; q.pop();
		if(vis[u])
			continue;
		vis[u] = 1;
		dp[u] = max(dp[u], -dis[u]);
		if(dis[u] <= dp[u]) {
			dp[u] = dp[0];
			continue;
		}
		for (int i = 0; i < e[u].size(); i++) {
			Edge v = e[u][i];
			if(dp[v.to] > dp[u] + v.l) {
				dp[v.to] = dp[u] + v.l;
				q.push(node{v.to, dp[v.to]});
			}
		}
	}
	return dp[n] < dis[n];
}
void solve() {
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++)
		e[i].clear();
	for (int i = 1; i <= m; i++) {
		int x, y, l, r; cin >> x >> y >> l >> r;
		e[x].push_back(Edge{y, l, r});
		e[y].push_back(Edge{x, l, r});
	}
	dijkstra1(k);
	cout << (dijkstra2(1) ? "Yes" : "No") << endl;
}
signed main() {
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}
posted @ 2026-01-08 21:13  LUlululu1616  阅读(2)  评论(0)    收藏  举报