#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const long long INF = 1e18;
const int MAXN = 100005;
const int LOG = 20;
int n, m, k, q;
vector<pair<int, long long>> adj[MAXN];
vector<pair<int, long long>> tree_adj[MAXN];
long long dist_npp[MAXN];
// Cấu trúc cạnh cho Kruskal
struct Edge {
int u, v;
long long w;
bool operator<(const Edge& other) const {
return w > other.w; // Sắp xếp giảm dần để tìm Cây khung lớn nhất
}
};
vector<Edge> edges;
// Disjoint Set Union (DSU)
int parent_node[MAXN], sz[MAXN];
void make_set() {
for (int i = 1; i <= n; ++i) {
parent_node[i] = i;
sz[i] = 1;
}
}
int find_set(int v) {
if (v == parent_node[v]) return v;
return parent_node[v] = find_set(parent_node[v]);
}
bool union_sets(int a, int b) {
a = find_set(a);
b = find_set(b);
if (a != b) {
if (sz[a] < sz[b]) swap(a, b);
parent_node[b] = a;
sz[a] += sz[b];
return true;
}
return false;
}
// Lowest Common Ancestor (LCA)
int up[MAXN][LOG];
long long min_w[MAXN][LOG];
int depth[MAXN];
void dfs(int u, int p, int d) {
depth[u] = d;
up[u][0] = p;
for (int i = 1; i < LOG; ++i) {
up[u][i] = up[up[u][i - 1]][i - 1];
min_w[u][i] = min(min_w[u][i - 1], min_w[up[u][i - 1]][i - 1]);
}
for (auto edge : tree_adj[u]) {
int v = edge.first;
long long weight = edge.second;
if (v != p) {
min_w[v][0] = weight;
dfs(v, u, d + 1);
}
}
}
// Truy vấn giá trị nhỏ nhất trên đường đi
long long query(int u, int v) {
long long res = INF;
if (depth[u] < depth[v]) swap(u, v);
// Nâng u lên cùng độ sâu với v
for (int i = LOG - 1; i >= 0; --i) {
if (depth[up[u][i]] >= depth[v]) {
res = min(res, min_w[u][i]);
u = up[u][i];
}
}
if (u == v) return res;
// Nâng cả u và v lên gần LCA
for (int i = LOG - 1; i >= 0; --i) {
if (up[u][i] != up[v][i]) {
res = min(res, min_w[u][i]);
res = min(res, min_w[v][i]);
u = up[u][i];
v = up[v][i];
}
}
// Xét nốt 2 cạnh nối u, v với LCA
res = min({res, min_w[u][0], min_w[v][0]});
return res;
}
int main() {
// Tối ưu I/O cho Competitive Programming
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, 0});
}
cin >> k;
vector<int> npp(k);
for (int i = 0; i < k; ++i) cin >> npp[i];
// 1. Multi-source Dijkstra
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) {
dist_npp[npp[i]] = 0;
pq.push({0, npp[i]});
}
while (!pq.empty()) {
pair<long long, int> top = pq.top();
pq.pop();
long long d = top.first;
int u = top.second;
if (d > dist_npp[u]) continue;
for (auto edge : adj[u]) {
int v = edge.first;
long long w = edge.second;
if (dist_npp[v] > dist_npp[u] + w) {
dist_npp[v] = dist_npp[u] + w;
pq.push({dist_npp[v], v});
}
}
}
// 2. Maximum Spanning Tree (Kruskal)
for (int i = 0; i < m; ++i) {
edges[i].w = min(dist_npp[edges[i].u], dist_npp[edges[i].v]);
}
sort(edges.begin(), edges.end());
make_set();
for (auto edge : edges) {
if (union_sets(edge.u, edge.v)) {
tree_adj[edge.u].push_back({edge.v, edge.w});
tree_adj[edge.v].push_back({edge.u, edge.w});
}
}
// 3. Khởi tạo LCA & Mảng Sparse Table
depth[0] = -1;
for (int i = 0; i < LOG; ++i) {
up[0][i] = 0;
min_w[0][i] = INF;
}
dfs(1, 0, 0); // Giả sử đỉnh 1 luôn có trên đồ thị và làm gốc
// 4. Trả lời truy vấn
cin >> q;
while (q--) {
int u, v;
cin >> u >> v;
cout << query(u, v) << "\n";
}
return 0;
}