1. 程式人生 > 實用技巧 >洛谷 P5304 [GXOI/GZOI2019]旅行者

洛谷 P5304 [GXOI/GZOI2019]旅行者

J 國有 \(n\) 座城市,這些城市之間通過 \(m\) 條單向道路相連,已知每條道路的長度。

一次,居住在 J 國的 Rainbow 邀請 Vani 來作客。不過,作為一名資深的旅行者,Vani 只對 J 國的 \(k\) 座歷史悠久、自然風景獨特的城市感興趣。
為了提升旅行的體驗,Vani 想要知道他感興趣的城市之間「兩兩最短路」的最小值(即在他感興趣的城市中,最近的一對的最短距離)。

也許下面的劇情你已經猜到了——Vani 這幾天還要忙著去其他地方遊山玩水,就請你幫他解決這個問題吧。

寫點套路水題/kk

我們叫感興趣的城市為關鍵點

因為關鍵點之間的兩兩最短路的最小值一定是某兩個關鍵點之間的最短路,於是我們考慮把所有關鍵點分成兩個集合\(A,B\)

,建一個超級起點\(s\)連向集合\(A\)裡的所有點,集合\(B\)裡的點向超級終點\(t\)連邊,邊權都為\(0\),然後跑最短路,\(t\)的最短路就是\(A\)中的點走到\(B\)中的點的最短路。

然後一個優化就是考慮對關鍵點二進位制分組,因為最終答案一定存在某位二進位制不一樣,所以列舉二進位制位,把某一位是\(0\)放在\(A\)組,某一位是\(1\)放在\(B\)組(因為是有向圖,還得反過來做一次),然後跑最短路,就能得到答案了。

/*
_/_/_/_/    _/_/_/_/_/  _/_/_/
_/      _/      _/    _/      _/
_/      _/      _/    _/      _/
_/      _/      _/    _/      _/
_/      _/      _/    _/  _/  _/
_/      _/  _/  _/    _/    _/_/
_/_/_/_/      _/_/     _/_/_/_/_/

_/_/_/_/    _/    _/  _/      _/
_/      _/   _/  _/   _/_/  _/_/
_/      _/    _/_/    _/ _/_/ _/
_/      _/     _/     _/  _/  _/
_/      _/    _/_/    _/      _/
_/      _/   _/  _/   _/      _/
_/_/_/_/    _/    _/  _/      _/

_/_/_/_/_/ _/_/_/_/_/ _/_/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/         _/     _/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/     _/_/_/_/_/ _/_/_/_/_/

_/_/_/_/_/ _/_/_/_/_/ _/_/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/         _/     _/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/     _/_/_/_/_/ _/_/_/_/_/
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define mp make_pair
#define fi first
#define se second
const int N = 1e5;
const int M = 5e5;
const long long inf = 1e16;
using namespace std;
struct node
{
	int to,cost;
};
int T,n,m,k,a[N + 5],s,t,tmp[N + 5],cnt;
long long dis[N + 5],ans;
bool vis[N + 5];
priority_queue <pair<long long,int> > q;
vector <node> d[N + 5];
vector <node>::iterator it;
long long Dij()
{
	for (int i = 1;i <= N + 2;i++)
		dis[i] = inf,vis[i] = 0;
	dis[s] = 0;
	q.push(mp(0,s));
	while (!q.empty())
	{
		int u = q.top().se;
		q.pop();
		if (vis[u])
			continue;
		vis[u] = 1;
		for (it = d[u].begin();it != d[u].end();it++)
		{
			int v = it -> to,w = it -> cost;
			if (dis[u] + w < dis[v])
			{
				dis[v] = dis[u] + w;
				q.push(mp(-dis[v],v));
			}
		}
	}
	return dis[t];
}
int main()
{
	scanf("%d",&T);
	s = N + 1;
	t = N + 2;
	while (T--)
	{
		scanf("%d%d%d",&n,&m,&k);
		int u,v,w;
		ans = inf;
		for (int i = 1;i <= N + 2;i++)
			d[i].clear();
		for (int i = 1;i <= m;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			d[u].push_back((node){v,w});
		}
		for (int i = 1;i <= k;i++)
			scanf("%d",&a[i]);
		for (int i = 0;(1 << i) <= n;i++)
		{
			cnt = 0;
			for (int j = 1;j <= k;j++)
				if (a[j] & (1 << i))
					d[s].push_back((node){a[j],0});
				else
				{
					d[a[j]].push_back((node){t,0});
					tmp[++cnt] = a[j];
				}
			ans = min(Dij(),ans);
			d[s].clear();
			for (int j = 1;j <= cnt;j++)
				d[tmp[j]].pop_back();
			cnt = 0;
			for (int j = 1;j <= k;j++)
				if (a[j] & (1 << i))
				{
					d[a[j]].push_back((node){t,0});
					tmp[++cnt] = a[j];
				}
				else
					d[s].push_back((node){a[j],0});
			ans = min(Dij(),ans);
			d[s].clear();
			for (int j = 1;j <= cnt;j++)
				d[tmp[j]].pop_back();
		}
		cout<<ans<<endl;
	}
	return 0;
}