#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const long long INF = 1e18;
struct OriginalEdge {
int u, v;
long long w;
};
struct Edge {
int u, v;
long long w;
bool operator<(const Edge& other) const {
return w > other.w; // Sort descending
}
};
const int MAXN = 100005;
int n, m, k, q;
vector<pair<int, long long>> adj[MAXN];
long long dist_npp[MAXN];
vector<OriginalEdge> edges;
// DSU structures
int parent_dsu[2 * MAXN];
int find_set(int v) {
if (v == parent_dsu[v])
return v;
return parent_dsu[v] = find_set(parent_dsu[v]);
}
// Kruskal Reconstruction Tree structures
vector<int> tree_adj[2 * MAXN];
long long val[2 * MAXN];
int up[2 * MAXN][20];
int depth_node[2 * MAXN];
void dfs(int v, int p, int d) {
up[v][0] = p;
for (int i = 1; i < 20; ++i) {
up[v][i] = up[up[v][i - 1]][i - 1];
}
depth_node[v] = d;
for (int u : tree_adj[v]) {
if (u != p) {
dfs(u, v, d + 1);
}
}
}
int get_lca(int u, int v) {
if (depth_node[u] < depth_node[v]) {
swap(u, v);
}
int diff = depth_node[u] - depth_node[v];
for (int i = 0; i < 20; ++i) {
if ((diff >> i) & 1) {
u = up[u][i];
}
}
if (u == v) return u;
for (int i = 19; i >= 0; --i) {
if (up[u][i] != up[v][i]) {
u = up[u][i];
v = up[v][i];
}
}
return up[u][0];
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
if (!(cin >> n >> m)) return 0;
for (int i = 0; i < m; ++i) {
int u, v;
long long w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
edges.push_back({u, v, w});
}
cin >> k;
for (int i = 1; i <= n; ++i) {
dist_npp[i] = INF;
}
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;
for (int i = 0; i < k; ++i) {
int g;
cin >> g;
dist_npp[g] = 0;
pq.push({0, g});
}
// 1. Multi-source Dijkstra
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist_npp[u]) continue;
for (auto& edge : adj[u]) {
int v = edge.first;
long long weight = edge.second;
if (dist_npp[u] + weight < dist_npp[v]) {
dist_npp[v] = dist_npp[u] + weight;
pq.push({dist_npp[v], v});
}
}
}
// 2. Prepare edges with bottleneck weight derived from D[u] and D[v]
vector<Edge> kruskal_edges;
for (const auto& e : edges) {
kruskal_edges.push_back({e.u, e.v, min(dist_npp[e.u], dist_npp[e.v])});
}
sort(kruskal_edges.begin(), kruskal_edges.end());
// 3. Build Kruskal Reconstruction Tree
for (int i = 1; i <= 2 * n; ++i) {
parent_dsu[i] = i;
}
int next_node = n + 1;
for (const auto& e : kruskal_edges) {
int pu = find_set(e.u);
int pv = find_set(e.v);
if (pu != pv) {
val[next_node] = e.w;
parent_dsu[pu] = next_node;
parent_dsu[pv] = next_node;
tree_adj[next_node].push_back(pu);
tree_adj[next_node].push_back(pv);
next_node++;
}
}
// 4. Set up binary lifting arrays (the tree root is next_node - 1)
int root = next_node - 1;
dfs(root, root, 0);
// 5. Answer Queries
cin >> q;
for (int i = 0; i < q; ++i) {
int s, t;
cin >> s >> t;
int lca_node = get_lca(s, t);
cout << val[lca_node] << "\n";
}
return 0;
}