P5304 [GXOI/GZOI2019]旅行者 题解

这题有 \(O(Tn\log n)\) 做法。

考虑类似求次短路的方式,对于每个点 \(i\) 求出能到它最近的关键点 \(x_i\),它能到最近的关键点 \(y_i\),即 \(\forall v,dis_{v\to i}\ge dis_{x_i\to i},dis_{i\to v}\ge dis_{i\to y_i}\),这一步正反图多源最短路跑两遍即可。

然后枚举每一条边 \((u,v,w)\),如果 \(x_u\ne y_v\cap x_u\ne0\cap y_v\ne0\),那么说明 \(x_u\to y_v\) 经过这条边并且这条路径一定是最短路,更新答案即可。

可以证明,答案一定会被包含并且非法状态不会被包含。

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P5304 [GXOI/GZOI2019]旅行者
	Date:2022/10/2
========= Plozia =========
*/

#include <bits/stdc++.h>
typedef long long LL;
using std::priority_queue;

const int MAXN = 1e5 + 5, MAXM = 5e5 + 5;
int n, m, k, Head[MAXN], cntEdge, Point[MAXN], Color[2][MAXN];
LL dis[2][MAXN];
bool book[MAXN];
struct node { int To; LL val; int Next; } Edge[MAXM];
struct EDGE { int x, y, z; } e[MAXM];
struct pri { int x; LL dis; bool operator <(const pri &fir)const { return (dis > fir.dis); } } ;

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
	return sum * fh;
}
LL Min(LL fir, LL sec) { return (fir < sec) ? fir : sec; }
void add(int x, int y, int z) { ++cntEdge; Edge[cntEdge] = (node){y, z, Head[x]}; Head[x] = cntEdge; }

void Dijkstra(int opt)
{
	priority_queue <pri> q;
	for (int i = 1; i <= n; ++i) book[i] = 0, dis[opt][i] = 0x7f7f7f7f7f7f7f7f, Color[opt][i] = 0;
	for (int i = 1; i <= k; ++i) dis[opt][Point[i]] = 0, Color[opt][Point[i]] = Point[i], q.push((pri){Point[i], 0});
	while (!q.empty())
	{
		int x = q.top().x; q.pop(); if (book[x]) continue ; book[x] = 1;
		for (int i = Head[x]; i; i = Edge[i].Next)
		{
			int u = Edge[i].To; if (book[u]) continue ;
			if (dis[opt][u] > dis[opt][x] + Edge[i].val)
			{
				dis[opt][u] = dis[opt][x] + Edge[i].val;
				Color[opt][u] = Color[opt][x]; q.push((pri){u, dis[opt][u]});
			}
		}
	}
}

void Solve()
{
	n = Read(), m = Read(), k = Read();
	for (int i = 1; i <= m; ++i) { e[i].x = Read(), e[i].y = Read(), e[i].z = Read(); add(e[i].x, e[i].y, e[i].z); }
	for (int i = 1; i <= k; ++i) Point[i] = Read();
	Dijkstra(0); cntEdge = 0; for (int i = 1; i <= n; ++i) Head[i] = 0;
	for (int i = 1; i <= m; ++i) add(e[i].y, e[i].x, e[i].z);
	Dijkstra(1); LL ans = 0x7f7f7f7f7f7f7f7f;
	for (int i = 1; i <= m; ++i)
	{
		if (Color[0][e[i].x] != 0 && Color[1][e[i].y] != 0 && Color[0][e[i].x] != Color[1][e[i].y]) ans = Min(ans, dis[0][e[i].x] + dis[1][e[i].y] + e[i].z);
	}
	printf("%lld\n", ans); cntEdge = 0; for (int i = 1; i <= n; ++i) Head[i] = 0;
}

int main()
{
	int t = Read(); while (t--) Solve(); return 0;
}
posted @ 2022-10-02 14:44  Plozia  阅读(39)  评论(0编辑  收藏  举报