Problem:
JOI 国のお祭り事情(Festivals in JOI Kingdom)

JOI 国にはN 個の街があり，それらの間はM 本の双方向に通行可能な道路で結ばれている．国民は道路を通ってそれらの街を移動する．

JOI 国の国民には，お祭りが好きな人が多く，現在，K 個の街でお祭りが開催されており，非常に賑わっている．一方で，一部の国民はお祭りを騒がしいとして嫌い，お祭りにできるだけ近づきたくないと思っている．

そこで国王は，優秀なプログラマーであるあなたに，そのようなお祭りを嫌う国民のため，ある街からある街に移動するために，お祭りが開催されている街にどれだけ近づかずに移動することができるかを高速に調べることのできるプログラムの作成を依頼した．

課題

道路の情報とお祭りが開催されている街の情報，およびQ 個のクエリ（出発する街Si と行きたい街Tiの組）が与えられる．各クエリi に対し，街Si から街Ti へのすべての経路のうち，お祭りまでの距離が最大となる経路の，お祭りまでの距離を求めるプログラムを作成せよ．ただし，ある経路のお祭りまでの距離とは，経路上の街からお祭りが開催されている街までの移動距離の最小値のことである．

制限

2 ≤ N ≤ 100000 (= 105)     JOI 国の街の個数

1 ≤ M ≤ 200000 (= 2 × 105)     JOI 国の道路の本数

1 ≤ K ≤ N     お祭りが開催されている街の個数

1 ≤ Q ≤ 100000 (= 105)     クエリの個数

1 ≤ Li ≤ 1000     i 番目の道路の長さ

入力

標準入力から以下のデータを読み込め．

1 行目には整数N, M, K, Q が空白を区切りとして書かれている．N はJOI 国の街の個数を，M はJOI国の道路の本数を，K はお祭りが開催されている街の個数を，Q はクエリの個数をそれぞれ表す．街には1, 2, ... , N の番号がつけられている．

続くM 行は道路の情報を表す．i + 1 行目(1 ≤ i ≤ M) には整数Ai, Bi, Li (1 ≤ Ai ≤ N, 1 ≤ Bi ≤ N) が空白を区切りとして書かれている．これは，i 番目の道路が街Ai と街Bi を結んでおり，長さがLi であることを表す．道路の両端が同じ街であることはない．また，任意の2 つの街p, q に対し，p とqを結ぶ道路は2 本以上存在しない．どの街からどの街へもいくつかの道路をたどって行くことができる．

続くK 行はお祭りが開催されている街の情報を表す．i + M + 1 行目(1 ≤ i ≤ K) には1 つの整数Fi(1 ≤ Fi ≤ N) が書かれている．これは街Fi でお祭りが開催されていることを表す．F1, ..., FK の中に同じ値が2 回以上現れることはない．

続くQ 行はクエリを表す．i + M + K + 1 行目(1 ≤ i ≤ Q) には2 つの整数Si, Ti (1 ≤ Si ≤ N, 1 ≤ Ti ≤ N, Si ≠ Ti) が空白を区切りとして書かれている．これはi 番目のクエリの出発する街がSi であり行きたい街がTi であることを表す．

出力

標準出力に，全クエリへの答えをQ 行で出力せよ．すなわち，i 行目に，街Si から街Ti へのすべての経路のうち，お祭りまでの距離が最大となる経路の，お祭りまでの距離を表す整数を出力せよ．

採点基準

採点用データのうち，

配点の10% 分については，Q = 1 を満たす．

配点の20% 分については，N ≤ 5000, Q ≤ 5000 を満たす．

配点の30% 分については，これら2 つの条件の少なくとも一方を満たす．また，これら2 つの条件の両方を満たすような採点用データはない．

入出力例

入力例 1

6 6 2 3
1 2 5
2 3 4
2 4 6
3 5 9
4 5 3
5 6 7
1
6
3 4
5 2
1 4

出力例 1

7
5
0

6 つの街が6 本の道路で結ばれており，お祭りは街1, 6 の2 つの街で開催されている．クエリは以下の3 つである．

1 つ目のクエリは街3 から街4 へ行くというものである．街2 を経由する経路ではお祭りまでの距離は5，街5 を経由する経路ではお祭りまでの距離は7 となるため，答えは7 である．

2 つ目のクエリは街5 から街2 へ行くというものである．街3 と街4 のどちらを経由しても，街2 でお祭りまでの距離が最小となり，答えは5 である．

3 つ目のクエリは街1 から街4 へ行くというものである．街1 はお祭りが開催されている街であるため，答えは0 となる．

図1: 入力例1

入力例 2

12 17 2 5
1 3 6
1 6 7
2 3 8
2 4 4
2 8 11
2 12 2
3 6 3
3 7 8
3 11 2
4 12 2
5 10 3
6 10 5
8 9 6
8 12 7
9 10 6
11 9 10
12 9 5
8
7
2 6
5 2
1 10
8 9
9 4

出力例 2

8
8
11
0
6

図1: 入力例2

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C++14 (judge verdict: Wrong Answer)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct UnionFind {
	vector<int> par;
	int cnt;
	UnionFind(int size_) : par(size_, -1), cnt(size_) {}
	void unite(int x, int y) {
		if((x = find(x)) != (y = find(y))) {
			if(par[y] < par[x]) swap(x, y);
			par[x] += par[y]; par[y] = x; cnt--;
		}
	}
	bool same(int x, int y) { return find(x) == find(y); }
	int find(int x) { return par[x] < 0 ? x : par[x] = find(par[x]); }
	int size(int x) { return -par[find(x)]; }
	int size() { return cnt; }
};

const int MAX_V = 100010;
const int MAX_LOG_V = 30;
const int INF = 1e9;

vector<int> dist(100000);
vector<vector<int>> parent(MAX_LOG_V, vector<int>(MAX_V));
vector<vector<int>> mind(MAX_LOG_V, vector<int>(MAX_V));
vector<int> depth(MAX_V);

class LCA {
public:
	vector<int> G[MAX_V];
	int root;

	void dfs(int v, int p, int d) {
		parent[0][v] = p;
		depth[v] = d;
		for(int i = 0; i < G[v].size(); i++) {
			if(G[v][i] != p) dfs(G[v][i], v, d + 1);
		}
	}

	void add_edge(int a, int b) {
		G[a].push_back(b);
		G[b].push_back(a);
	}

	void init(int V, int root = 0) {
		this->root = root;
		dfs(root, -1, 0);
		for(int i = 0; i < V; i++) {
			for(int j = 0; j < MAX_LOG_V; j++) {
				mind[j][i] = INF;
			}
			mind[0][i] = dist[i];
		}
		for(int k = 0; k + 1 < MAX_LOG_V; k++) {
			for(int v = 0; v < V; v++) {
				if(parent[k][v] < 0) parent[k + 1][v] = -1;
				else {
					int p = parent[k][parent[k][v]];
					parent[k + 1][v] = p;
					if(p >= 0) {
						mind[k + 1][v] = min({ mind[k][v], mind[k][parent[k][v]], dist[p] });
					}
				}
			}
		}
	}

	int lca(int u, int v) {
		if(depth[u] > depth[v]) swap(u, v);
		for(int k = 0; k < MAX_LOG_V; k++) {
			if((depth[v] - depth[u]) >> k & 1) {
				v = parent[k][v];
			}
		}
		if(u == v) return u;
		for(int k = MAX_LOG_V - 1; k >= 0; k--) {
			if(parent[k][u] != parent[k][v]) {
				u = parent[k][u];
				v = parent[k][v];
			}
		}
		return parent[0][u];
	}
};

typedef pair<int, int> P;

struct edge {
	int from, to, d;
	bool operator <(const edge& x) const {
		return d > x.d;
	}
};

vector<edge> G[100000];
edge es[200000];

LCA lca;

int main() {
	int N, M, K, Q;
	scanf("%d%d%d%d", &N, &M, &K, &Q);

	for(int i = 0; i < M; i++) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		a--, b--;
		G[a].push_back({ a, b, c });
		G[b].push_back({ b, a, c });
		es[i] = { a, b, -1 };
	}

	fill(dist.begin(), dist.end(), INF);
	priority_queue <P, vector<P>, greater<P>> q;
	for(int i = 0; i < K; i++) {
		int a;
		scanf("%d", &a);
		a--;
		dist[a] = 0;
		q.push({ 0, a });
	}
	while(q.size()) {
		int v = q.top().second, d = q.top().first;
		q.pop();
		if(dist[v] < d) continue;
		for(auto& e : G[v]) {
			if(dist[e.to] > d + e.d) {
				dist[e.to] = d + e.d;
				q.push({ d + e.d, e.to });
			}
		}
	}

	for(int i = 0; i < M; i++) {
		int u = es[i].from, v = es[i].to;
		es[i].d = min(dist[u], dist[v]);
	}
	sort(es, es + M);

	UnionFind uf(N);
	for(int i = 0; i < M; i++) {
		int u = es[i].from, v = es[i].to;
		if(uf.same(u, v)) continue;
		uf.unite(u, v);
		lca.add_edge(u, v);
	}
	lca.init(N);

	while(Q--) {
		int a, b;
		cin >> a >> b;
		a--, b--;

		int x = lca.lca(a, b);
		int s[2] = { a, b };
		int ans = INF;
		for(int i = 0; i < 2; i++) {
			int v = s[i];
			ans = min(ans, dist[v]);
			int d = depth[v] - depth[x];
			for(int l = 0; l < 21; l++) {
				if(d >> l & 1) {
					ans = min(ans, mind[l][v]);
					v = parent[l][v];
					assert(v >= 0);
				}
			}
		}
		cout << ans << endl;
	}
}