[GXOI/GZOI2019] 旅行者

题目传送门

非常套路的做法。

做法

做法\(1\)

考虑直接将\(k\)个标记点当做起点进行\(Dijkstra\)会出现问题(因为把每个标记点的\(dis\)值设为\(0\)就不会在更新了,与题意说的起点和终点均为标记点且为两个不同的点不符。)

那我们看到我们要求\(k\)个标记点的最短路,那么就设起点为\(s\)\(t\),且\(s \neq t\)\(s\)\(t\)均为标记点,\(s\)\(t\)的最短路为\(k\)个标记点的最短路的最小值。

那么最直接的做法是随机讲这\(k\)个点分为两个部分,一个部分的点的\(dis\)值设为\(0\),再进行\(Dijkstra\),最后取另一部分的\(dis\)值的最小值与答案\(ans\)进行比较。这样做的正确性即为起点在\(dis\)值为\(0\)的部分且终点在另一部分,即每次操作有$ \frac{1}{2} \times \frac{1}{2} = \frac{1}{4}$的概率正确。所以多进行几次这样的操作就行了。

做法\(2\)

做法\(2\)为做法\(1\)的优化,且正确性有保障。

在这之前先引入一个概念二进制分组,因为要使\(s\)\(t\)不相同,所以\(s\)\(t\)拆分为二进制数后至少有一位不相同。所以我们直接按照每个二进制位是\(0\)还是\(1\)进行划分即可。

但值得注意的是本题的边为有向边,所以在划分时有可能把终点的\(dis\)值设为\(0\),所以这样就显然会有问题,那就在反过来再跑一次\(Dijkstra\)就行了。

\(Code\)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>

#define x first
#define y second
#define mp make_pair

using namespace std;
const int N = 1e5 + 10, M = 5e5 + 10;
typedef long long ll;
typedef pair<ll,int> PLI;
const ll INF = 1e15;

struct node{
	ll d;
	int x, st;
	bool operator > (const node& t) const
	{
		return d > t.d;
	}
};


PLI d[N][2];
int v[N][2], head[N], ver[M], Next[M], edge[M], tot, n, m, k, a[N];

void add(int x, int y, int z)
{
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}

void dijkstra()
{
	priority_queue <node, vector<node>, greater<node> > q;
	for(int i = 1; i <= n; i++) v[i][0] = v[i][1] = 0, d[i][0] = d[i][1] = mp(INF, 0);
	sort(a + 1, a + k + 1);
	for(int i = 1; i <= k; i++)
	{
		if(a[i] == a[i - 1]) continue;
		int x = a[i];
		d[x][0] = mp(0ll, x);
		q.push(node({0, x, x}));
	}
	while(!q.empty())
	{
		node top = q.top(); q.pop();
		int x = top.x, st = top.st; ll dis = top.d;
		if((v[x][0] && v[x][1]) || (v[x][0] && st == d[x][0].y)) continue;
		if(!v[x][0])
		{
			v[x][0] = 1;
			d[x][0] = mp(dis, st);
		}
		else
		{
			v[x][1] = 1;
			d[x][1] = mp(dis, st);
		}
		for(int i = head[x]; i; i = Next[i])
		{
			int y = ver[i], z = edge[i];
			PLI t = mp(dis + z, st);
			if(t < d[y][0])
			{
				if(t.y != d[y][0].y) d[y][1] = d[y][0];
				d[y][0] = t;
				q.push(node({dis + z, y, st}));
			}
			else if(t < d[y][1] && st != d[y][0].y)
			{
				d[y][1] = t;
				q.push(node({dis + z, y, st}));
			}
		}
	}
}

int read()
{
	int x = 0, t = 1; char ch = getchar();
	while(ch < '0' || ch > '9')
	{
		if(ch == '-') t = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * t;
}

int main()
{
	int T = read();
	while(T--)
	{
		tot = 0;
		n = read(), m = read();k = read();
		for(int i = 1; i <= n; i++) head[i] = 0;
		for(int i = 1; i <= m; i++)
		{
			int x = read(), y = read(), z = read();
			add(x, y, z);
		} 
		for(int i = 1; i <= k; i++) a[i] = read();
		dijkstra();
		ll res = INF;
		for(int i = 1; i <= k; i++) res = min(res, d[a[i]][1].x);
		printf("%lld\n", res);
	}
	return 0;
}
posted @ 2023-12-30 14:28  CQWYB  阅读(6)  评论(0编辑  收藏  举报