Problem:
JOI 国のお祭り事情(Festivals in JOI Kingdom)
JOI 国にはN 個の街があり，それらの間は
M
本の双方向に通行可能な道路で結ばれている．国民は道路を通ってそれらの街を移動する．
JOI 国の国民には，お祭りが好きな人が多く，現在，
K
個の街でお祭りが開催されており，非常に賑わっている．一方で，一部の国民はお祭りを騒がしいとして嫌い，お祭りにできるだけ近づきたくないと思っている．
そこで国王は，優秀なプログラマーであるあなたに，そのようなお祭りを嫌う国民のため，ある街からある街に移動するために，お祭りが開催されている街にどれだけ近づかずに移動することができるかを高速に調べることのできるプログラムの作成を依頼した．
課題
道路の情報とお祭りが開催されている街の情報，および
Q
個のクエリ（出発する街
S
i
と行きたい街
T
i
の組）が与えられる．各クエリ
i
に対し，街
S
i
から街
T
i
へのすべての経路のうち，お祭りまでの距離が最大となる経路の，お祭りまでの距離を求めるプログラムを作成せよ．ただし，ある経路のお祭りまでの距離とは，経路上の街からお祭りが開催されている街までの移動距離の最小値のことである．
制限
2 ≤
N
≤ 100000 (= 10
5
)     JOI 国の街の個数
1 ≤
M
≤ 200000 (= 2 × 10
5
)     JOI 国の道路の本数
1 ≤
K
≤
N
お祭りが開催されている街の個数
1 ≤
Q
≤ 100000 (= 10
5
)     クエリの個数
1 ≤
L
i
≤ 1000
i
番目の道路の長さ
入力
標準入力から以下のデータを読み込め．
1 行目には整数
N
,
M
,
K
,
Q
が空白を区切りとして書かれている．
N
はJOI 国の街の個数を，
M
はJOI国の道路の本数を，
K
はお祭りが開催されている街の個数を，
Q
はクエリの個数をそれぞれ表す．街には1, 2, ... ,
N
の番号がつけられている．
続く
M
行は道路の情報を表す．
i
+ 1 行目(1 ≤
i
≤
M
) には整数
A
i
,
B
i
,
L
i
(1 ≤
A
i
≤
N
, 1 ≤
B
i
≤
N
) が空白を区切りとして書かれている．これは，
i
番目の道路が街
A
i
と街
B
i
を結んでおり，長さが
L
i
であることを表す．道路の両端が同じ街であることはない．また，任意の2 つの街
p
,
q
に対し，
p
と
q
を結ぶ道路は2 本以上存在しない．どの街からどの街へもいくつかの道路をたどって行くことができる．
続く
K
行はお祭りが開催されている街の情報を表す．
i
+
M
+ 1 行目(1 ≤
i
≤
K
) には1 つの整数
F
i
(1 ≤
F
i
≤
N
) が書かれている．これは街
F
i
でお祭りが開催されていることを表す．
F
1
, ...,
F
K
の中に同じ値が2 回以上現れることはない．
続く
Q
行はクエリを表す．
i
+
M
+
K
+ 1 行目(1 ≤
i
≤
Q
) には2 つの整数
S
i
,
T
i
(1 ≤
S
i
≤
N
, 1 ≤
T
i
≤
N
,
S
i
≠
T
i
) が空白を区切りとして書かれている．これは
i
番目のクエリの出発する街が
S
i
であり行きたい街が
T
i
であることを表す．
出力
標準出力に，全クエリへの答えを
Q
行で出力せよ．すなわち，
i
行目に，街
S
i
から街
T
i
へのすべての経路のうち，お祭りまでの距離が最大となる経路の，お祭りまでの距離を表す整数を出力せよ．
採点基準
採点用データのうち，
配点の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
問題文と自動審判に使われるデータは、
情報オリンピック日本委員会
が作成し公開している問題文と採点用テストデータです。

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

using P = pair<int, int>;

class union_find {
public:
    union_find(int n)
        : par_(n, -1)
    {}
    void init(int n) {
        par_.assign(n, -1);
    }

    int root(int x) {
        return par_[x] < 0 ? x : par_[x] = root(par_[x]);
    }

    bool unite(int x, int y) {
        x = root(x); y = root(y);
        if(x == y) {
            return false;
        } else {
            if(par_[x] < par_[y]) {
                par_[x] += par_[y];
                par_[y] = x;
            } else {
                par_[y] += par_[x];
                par_[x] = y;
            }
            return true;
        }
    }

    bool same(int x, int y) {
        return root(x) == root(y);
    }

    int size(int x) {
        return -par_[root(x)];
    }

private:
    std::vector<int> par_;
};


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

using edges = vector<edge>;
using graph = vector<edges>;


class lca {
public:
    lca(vector<vector<int>>& g, int root)
        : V(g.size()),
          log_V(0),
          depth_(V, 0)
    {
        for(int v=V; v>0; v /= 2) {
            ++log_V;
        }
        parent.assign(log_V, std::vector<int>(V, 0));
        dfs(g, root, -1, 0);
        for(int k=0; k+1 < log_V; ++k) {
            for(int v=0; v<V; ++v) {
                if(parent[k][v] < 0) {
                    parent[k+1][v] = -1;
                } else {
                    parent[k+1][v] = parent[k][parent[k][v]];
                }
            }
        }
    }

    void dfs(vector<vector<int>> const& g, 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, g[v][i], v, d+1);
            }
        }
    }

    int depth(int v) const {
        return depth_[v];
    }

    int query(int u, int v) {
        if(depth_[u] > depth_[v]) {
            std::swap(u, v);
        }
        for(int k=0; k<log_V; ++k) {
            if((depth_[v] - depth_[u]) >> k & 1) {
                v = parent[k][v];
            }
        }
        if(u == v) {
            return u;
        }
        for(int k=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];
    }

private:
    int V, log_V;
    std::vector<vector<int>> parent;
    std::vector<int> depth_;
};


constexpr int INF = 1e9;

int main() {
    int N, M, K, Q;
    cin >> N >> M >> K >> Q;
    graph g(N);
    for(int i=0; i<M; ++i) {
        int a, b, l;
        cin >> a >> b >> l;
        a--; b--;
        g[a].push_back((edge){a, b, l});
        g[b].push_back((edge){b, a, l});
    }

    vector<int> d(2*N, INF);
    priority_queue<P, vector<P>, greater<P>> que;
    for(int i=0; i<K; ++i) {
        int f;
        cin >> f;
        f--;
        d[f] = 0;
        que.push(make_pair(0, f));
    }
    while(!que.empty()) {
        P p = que.top();
        que.pop();
        int v = p.second;
        if(d[v] < p.first) {
            continue;
        }
        for(auto& e : g[v]) {
            if(d[e.to] > d[v] + e.cost) {
                d[e.to] = d[v] + e.cost;
                que.push(make_pair(d[e.to], e.to));
            }
        }
    }

    edges es;
    for(int i=0; i<N; ++i) {
        for(auto& e : g[i]) {
            if(i < e.to) {
                es.push_back((edge){i, e.to, min(d[i], d[e.to])});
            }
        }
    }
    sort(es.begin(), es.end());

    union_find uf(N);
    vector<vector<int>> tree(2*N);
    int new_id = N;
    vector<int> id(N);
    for(int i=0; i<N; ++i) {
        id[i] = i;
    }
    for(auto& e : es) {
        int a = uf.root(e.from), b = uf.root(e.to);
        if(uf.unite(a, b)) {
            tree[new_id].push_back(id[a]);
            tree[new_id].push_back(id[b]);
            d[new_id] = min(d[id[a]], d[id[b]]);
            id[uf.root(a)] = new_id;
            new_id += 1;
        }
    }

    lca l(tree, new_id-1);
    for(int i=0; i<Q; ++i) {
        int s, t;
        cin >> s >> t;
        s--; t--;
        cout << d[l.query(s, t)] << endl;
    }
}
accuracy: 1
time_ms: 290
memory_kb: 46300
final_rank: 9

code_snippet: #include <vector>
#include <iostream>
#include <queue>
#include <algorithm>
#define INF 1000000001LL
#define LNF 1000000000000000001LL
#define MOD 1000000007LL
#define MAX 200000
#define long long long
#define all(x) x.begin(),x.end()
using namespace std;


class Unionfind
{
public:
    vector<int> parent;
    
    void init(int size)
    {
        parent = vector<int>(size);
        for(int i = 0; i<size; i++)
            parent[i] = i;
    }
    int find(int x)
    {
        if(parent[x] == x)
            return x;
        return parent[x] = find(parent[x]);
    }
    
    void merge(int x, int y)
    {
        x = find(x);
        y = find(y);

        if(x == y)
            return;
        
        if(x < y)
            parent[y] = x;
        else
            parent[x] = y;
    }
    bool isMerge(int x, int y)
    {
        x = find(x);
        y = find(y);
        return x == y;
    }
};
vector<pair<int,int>> graph[100000];
pair<int,int> dp[20][100000];
int depth[100000];

void dfs(int x, int d, int par, int v)
{
    depth[x] = d;
    dp[0][x] = {par,v};

    for(int i = 0; i<graph[x].size(); i++)
    {
        int y = graph[x][i].first;
        int w = graph[x][i].second;

        if(y == par)
            continue;
        dfs(y,d+1,x,w);
    }
}

int lca(int x, int y)
{
    if(depth[x] > depth[y])
        swap(x,y);

    int diff = depth[y]-depth[x];

    int res = INF;

    for(int i = 0; i<20; i++)
    {
        if(diff&(1<<i))
        {
            res = min(res,dp[i][y].second);
            y = dp[i][y].first;
        }
    }
    if(x == y)
        return res;
    
    for(int i = 19; i>=0; i--)
    {
        if(dp[i][x].first != dp[i][y].first)
        {
            res = min(res,dp[i][x].second);
            res = min(res,dp[i][y].second);
            x = dp[i][x].first;
            y = dp[i][y].first;
        }
    }
    res = min(res,dp[0][x].second);
    res = min(res,dp[0][y].second);
    return res;
}
int main()
{
	ios_base::sync_with_stdio(0); 
    cin.tie(0);
    
    int n,m,k,q;
    cin >> n >> m >> k >> q;

    vector<pair<int,pair<int,int>>> edge(m);
    for(int i = 0; i<m; i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        a--;
        b--;
        graph[a].push_back({b,c});
        graph[b].push_back({a,c});

        edge[i] = {0,{a,b}};
    }

    priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq;
    vector<int> vis(n,INF);
    for(int i = 0; i<k; i++)
    {
        int x;
        cin >> x;
        x--;
        pq.push({0,x});
        vis[x] = 0;
    }
    while(!pq.empty())
    {
        int x = pq.top().second;
        int v = pq.top().first;
        pq.pop();
        if(vis[x] < v)
            continue;
        
        for(int i = 0; i<graph[x].size(); i++)
        {
            int y = graph[x][i].first;
            int w = graph[x][i].second;

            if(v+w < vis[y])
            {
                vis[y] = v+w;
                pq.push({v+w,y});
            }
        }
    }
    for(int i = 0; i<m; i++)
        edge[i].first = min(vis[edge[i].second.first],vis[edge[i].second.second]);
    sort(all(edge));
    reverse(all(edge));

    Unionfind uf;
    uf.init(n);
    for(int i = 0; i<n; i++)
        graph[i].clear();

    for(int i = 0; i<m; i++)
    {
        int c = edge[i].first;
        int a = edge[i].second.first;
        int b = edge[i].second.second;

        if(uf.isMerge(a,b))
            continue;
        
        uf.merge(a,b);
        graph[a].push_back({b,c});
        graph[b].push_back({a,c});
    }

    dfs(0,0,0,0);

    for(int i = 1; i<20; i++)
    {
        for(int j = 0; j<n; j++)
        {
            dp[i][j].first = dp[i-1][dp[i-1][j].first].first;
            dp[i][j].second = min(dp[i-1][dp[i-1][j].first].second,dp[i-1][j].second);
        }
    }

    while(q--)
    {
        int a,b;
        cin >> a >> b;
        a--;
        b--;
        cout << lca(a,b) << "\n";
    }
    return 0;
}
accuracy: 1
time_ms: 110
memory_kb: 31036
final_rank: 3

code_snippet: // #ifndef ONLINE_JUDGE
// #define _GLIBCXX_DEBUG//[]で配列外参照をするとエラーにしてくれる。上下のやつがないとTLEになるので注意 ABC311Eのサンプル4みたいなデバック中のTLEは防げないので注意
// #endif

#include <bits/stdc++.h>
using namespace std;
// #include <atcoder/all>
// using namespace atcoder;


template<typename T> using vc = vector<T>;//prioriy_queueに必要なのでここにこれ書いてます
template<typename T> using vv = vc<vc<T>>;

//-------------1.型系---------------
using ll = long long;
ll INF = 2e18;

// #include <boost/multiprecision/cpp_int.hpp>//インストール的なのをしてないとできないので注意
// namespace multip = boost::multiprecision;
// //using lll = multip::cpp_int;//無制限を使いたいときはこっちを使う
// using lll = multip::int128_t;

using ld = long double;
using bl = bool;
// using mint = modint998244353;
//using mint = modint1000000007;
//using mint = modint;//使うときはコメントアウトを外す
//mint::set_mod(m);//使うときはコメントアウトを外す

template<class T> using pq = priority_queue<T, vc<T>>;//大きい順
template<class T> using pq_g = priority_queue<T, vc<T>, greater<T>>;//小さい順
//-----------------------------------



//-------------2.配列系--------------
using vl = vc<ll>; using vvl = vv<ll>; using vvvl = vv<vl>; using vvvvl = vv<vvl>;
using vs = vc<string>; using vvs = vv<string>;
using vb = vc<bl>; using vvb = vv<bl>; using vvvb = vv<vb>;
using vld = vc<ld>; using vvld = vv<ld>; using vvvld = vv<vld>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
// using vmint = vc<mint>; using vvmint = vv<mint>; using vvvmint = vv<vmint>;

//配列外参照対策のやつは一番上の行にあります

#define rep(i,n) for(ll i = 0; i < (n); ++i)//↓でrepを使うので書いてます
template<class T>istream& operator>>(istream& i, vc<T>& v) { rep(j, size(v))i >> v[j]; return i; }

using ar2 = array<ll, 2>;

//----------------------------------



//--------3.コード短縮化とか---------
#define rep(i,n) for(ll i = 0; i < (n); ++i)
#define rrep(i,n) for(ll i = 1; i <= (n); ++i)
#define drep(i,n) for(ll i = (n)-1; i >= 0; --i)
#define nfor(i,s,n) for(ll i=s;i<n;i++)//i=s,s+1...n-1 ノーマルfor
#define dfor(i,s,n) for(ll i = (s)-1; i>=n;i--)//s-1スタートでnまで落ちる

#define nall(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()

#define chmax(x,y) x = max(x,y)
#define chmin(x,y) x = min(x,y)

#define pb push_back
#define eb emplace_back
#define em emplace
#define pob pop_back


#define YES cout<<"Yes"<<endl
#define NO cout<<"No"<<endl
#define YN {cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}// if(a==b)YN;
#define dame cout<<-1<<endl


#define vc_unique(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define vc_rotate(v) rotate(v.begin(),v.begin()+1,v.end());

#define pop_cnt(s) ll(popcount(uint64_t(s)))

#define next_p(v) next_permutation(v.begin(),v.end())

//if (regex_match(s, regex("")))YN;//文字列sの判定を行う。コメントアウトを外して「""」の中に判定する内容を入れる

//-------------------------------




//---------4.グリッド系----------
vl di = { 0,1,0,-1 };//vl di={0,1,1,1,0,-1,-1,-1};
vl dj = { 1,0,-1,0 };//vl dj={1,1,0,-1,-1,-1,0,1};

bool out_grid(ll i, ll j, ll h, ll w) {//trueならcontinue
    return (!(0 <= i && i < h && 0 <= j && j < w));
}

#define vvl_kaiten_r(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//時計回りに90°回転
#define vvl_kaiten_l(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//反時計周りに90°回転

#define vs_kaiten_r(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//文字列版 時計回りに90°回転
#define vs_kaiten_l(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//文字列版　反時計周りに90°回転


#define vvl_tenti(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][i]=v[i][j];swap(nx,v);}
#define vs_tenti(v) {ll n = size(v); vs nx(n, string(n,'.')); rep(i, n)rep(j, n)nx[j][i] = v[i][j]; swap(nx, v);}

//--------------------------------




//-----------5.数学系--------------
#define yu_qurid(x,y) ((x)*(x)+(y)*(y))//ユークリッド距離 sqrtはしてないなので注意
#define mannhattan(x1,x2,y1,y2) (abs(x1-x2)+abs(y1-y2)) // マンハッタン距離 = |x1-x2|+|y1-y2|

template<class T>T tousa_sum1(T l, T d, T r) {//初項,公差,末項 で総和を求める
    T wide = (r - l) / d + 1;
    return (l + r) * wide / 2;
}
template<class T>T tousa_sum2(T a, T d, T n) {//初項,交差,項数 で総和を求める
    return (a * 2 + d * (n - 1)) * n / 2;
}
ll kousa_kousuu(ll l, ll r, ll d) {//初項,末項,交差 で等差数列の項数を求める
    return (r - l) / d + 1;
}
// mint touhi_sum(mint a, mint r, ll n) {//初項,公比,項数で等比数列の総和を求める
//     if (r == 1) {
//         return a * n;
//     }
//     mint bunsi = a * (r.pow(n) - mint(1));
//     mint bunbo = r - 1;
//     return bunsi / bunbo;
// }

ll nc2(ll x) { return x * (x - 1) / 2; }
ll nc3(ll x) { return x * (x - 1) * (x - 2) / 6; }

//----------------------------------------------




//-----------6.デバックや出力系------------------
void print(ld x) { printf("%.20Lf\n", x); }

void mukou_debug(vvl to, bool yukou) {//GRAPH × GRAPH用の無向グラフを出力する
    ll n = size(to); ll cnt = 0;//辺の本数
    vc<pair<ll, ll>>v; rep(i, n)for (ll t : to[i])  if (i < t || yukou)cnt++, v.eb(i + 1, t + 1);//有向グラフなら全部OK、違うなら無向なのでf<tのみ見る、using Pのやつを別のにしたいときのためにPを使わずにpair<ll,ll>にしてる
    cout << n << ' ' << cnt << endl; for (auto [f, t] : v)cout << f << ' ' << t << endl;
}

#define vc_cout(v){ll n = size(v);rep(i,n)cout<<v[i]<<endl;}//一次元配列を出力する
#define vv_cout(v){ll n = size(v);rep(i,n){rep(j,size(v[i])){cout<<v[i][j]<<' ';}cout<<endl;}}//二次元配列を出力する

/*  とりあえず各町のお祭りからの距離を知りたいので、多始点ダイクストラみたいなことをする
    ダブリングらしいけどどうテーブルを作るか
    答えで二分探索したい気持ち
    dp[i][j]: 街jからお祭りまでの距離を2^i以下で移動できる街のリスト？
    Q≤1e5なので各クエリにO(log?)くらいでこたえたい

*/

struct UnionFind {
    vector<int> par, rank, siz;

    // 構造体の初期化
    UnionFind(int n) : par(n,-1), rank(n,0), siz(n,1) { }

    // 根を求める
    int root(int x) {
        if (par[x]==-1) return x; // x が根の場合は x を返す
        else return par[x] = root(par[x]); // 経路圧縮
    }

    // x と y が同じグループに属するか (= 根が一致するか)
    bool issame(int x, int y) {
        return root(x)==root(y);
    }

    // x を含むグループと y を含むグループを併合する
    bool unite(int x, int y) {
        int rx = root(x), ry = root(y); // x 側と y 側の根を取得する
        if (rx==ry) return false; // すでに同じグループのときは何もしない
        // union by rank
        if (rank[rx]<rank[ry]) swap(rx, ry); // ry 側の rank が小さくなるようにする
        par[ry] = rx; // ry を rx の子とする
        if (rank[rx]==rank[ry]) rank[rx]++; // rx 側の rank を調整する
        siz[rx] += siz[ry]; // rx 側の siz を調整する
        return true;
    }

    // x を含む根付き木のサイズを求める
    int size(int x) {
        return siz[root(x)];
    }
};

int main(){
    ll n, m, k, q;
    cin >> n >> m >> k >> q;
    vv<pll> G(n, vc<pll>());
    rep(i, m){
        ll a, b, l;
        cin >> a >> b >> l;
        a--; b--;
        G[a].pb(make_pair(b, l));
        G[b].pb(make_pair(a, l));
    }
    set<ll> st;
    rep(i, k){
        ll f;
        cin >> f;
        f--;
        st.insert(f);
    }
    vl s(q), t(q);
    rep(i, q){
        cin >> s[i] >> t[i];
        s[i]--; t[i]--;
    }

    // 各街のお祭りまでの距離を求める
    vl d(n, INF); // d[i]: 町iのお祭りまでの距離
    pq_g<pll> pq;
    for(ll u : st) pq.push(make_pair(0, u));
    vb seen(n, false);

    while(pq.size()){
        auto [costu, u] = pq.top();
        pq.pop();

        if(costu > d[u]) continue;
        d[u] = costu;
        seen[u] = true;

        for(auto [v, duv] : G[u]){
            if(seen[v]) continue;
            if(d[v] > d[u] + duv){
                d[v] = d[u] + duv;
                pq.push(make_pair(d[v], v));
            }
        }
    }

    // 辺をコストの降順でソート。コストが最大になる全域木Fをつくる。クラスカル法
    vc<tuple<ll, ll, ll>> edges;
    rep(u, n){
        for(auto [v, l] : G[u]){
            edges.pb(make_tuple(u, v, min(d[u], d[v])));
        }
    }

    sort(nall(edges), [](auto const &l, auto const &r){
        return get<2>(l) > get<2>(r);
    });

    vvl F(n, vl());
    UnionFind uf = UnionFind(n);
    for(auto [from, to, cost] : edges){
        if(!uf.issame(from, to)){
            uf.unite(from, to);
            F[from].pb(to);
            F[to].pb(from);
        }
    }

    // LCAの下準備
    ll maxLogN = 20;
    /*  dp[i][u]: Fを頂点0を根とする根付き木としてみたときの
        頂点uの2^i個上の親と,そのパス上でのコスト最小の頂点のペア
    */
    vv<pll> dp(maxLogN, vc<pll>(n));
    vl dist(n, -1);

    auto dfs = [&](auto self, ll u, ll p, ll dd) -> void{
        ll second = (p == -1 ? d[u] : min(d[p], d[u]));
        dp[0][u] = make_pair(p, second);
        dist[u] = dd;
        for(ll v : F[u]){
            if(v == p) continue;

            self(self, v, u, dd+1);
        }
        return;
    };

    dfs(dfs, 0, -1, 0);
    rep(i, maxLogN-1){
        rep(j, n){
            if(dp[i][j].first == -1){
                dp[i+1][j] = make_pair(-1, dp[i][j].second);
            }
            else{
                dp[i+1][j] = make_pair(dp[i][dp[i][j].first].first, min(dp[i][j].second, dp[i][dp[i][j].first].second));
            }
        }
    }

    // LCAの計算
    rep(qq, q){
        ll ans = INF;

        ll u = s[qq], v = t[qq];
        if(dist[u] < dist[v]) swap(u, v);
        rep(i, maxLogN){
            if(((dist[u] - dist[v]) >> i) & 1ll){
                chmin(ans, dp[i][u].second); // uを変更する前にUが上に行くパス上の最小値を調べる必要がある
                u = dp[i][u].first;
            }
        }

        if(u == v){
            cout << ans << endl;
            continue;    
        }
        drep(i, maxLogN){
            if(dp[i][u].first != dp[i][v].first){
                ans = min({ans, dp[i][u].second, dp[i][v].second}); // u, vを変更する前にu,vそれぞれが通ることになるパスを調べておく。
                u = dp[i][u].first;
                v = dp[i][v].first;
            }
        }
        // ans = min(ans, dp[0][u].seco;d);
        ans = min({ans, dp[0][u].second, dp[0][v].second});

        cout << ans << endl;
    }

    // vv_cout(F);
    // cout << endl;
    // vc_cout(dist);

    return 0;
}
accuracy: 1
time_ms: 320
memory_kb: 69184
final_rank: 14

code_snippet: // #ifndef ONLINE_JUDGE
// #define _GLIBCXX_DEBUG//[]で配列外参照をするとエラーにしてくれる。上下のやつがないとTLEになるので注意 ABC311Eのサンプル4みたいなデバック中のTLEは防げないので注意
// #endif

#include <bits/stdc++.h>
using namespace std;
// #include <atcoder/all>
// using namespace atcoder;


template<typename T> using vc = vector<T>;//prioriy_queueに必要なのでここにこれ書いてます
template<typename T> using vv = vc<vc<T>>;

//-------------1.型系---------------
using ll = long long;
ll INF = 2e18;

// #include <boost/multiprecision/cpp_int.hpp>//インストール的なのをしてないとできないので注意
// namespace multip = boost::multiprecision;
// //using lll = multip::cpp_int;//無制限を使いたいときはこっちを使う
// using lll = multip::int128_t;

using ld = long double;
using bl = bool;
// using mint = modint998244353;
//using mint = modint1000000007;
//using mint = modint;//使うときはコメントアウトを外す
//mint::set_mod(m);//使うときはコメントアウトを外す

template<class T> using pq = priority_queue<T, vc<T>>;//大きい順
template<class T> using pq_g = priority_queue<T, vc<T>, greater<T>>;//小さい順
//-----------------------------------



//-------------2.配列系--------------
using vl = vc<ll>; using vvl = vv<ll>; using vvvl = vv<vl>; using vvvvl = vv<vvl>;
using vs = vc<string>; using vvs = vv<string>;
using vb = vc<bl>; using vvb = vv<bl>; using vvvb = vv<vb>;
using vld = vc<ld>; using vvld = vv<ld>; using vvvld = vv<vld>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
// using vmint = vc<mint>; using vvmint = vv<mint>; using vvvmint = vv<vmint>;

//配列外参照対策のやつは一番上の行にあります

#define rep(i,n) for(ll i = 0; i < (n); ++i)//↓でrepを使うので書いてます
template<class T>istream& operator>>(istream& i, vc<T>& v) { rep(j, size(v))i >> v[j]; return i; }

using ar2 = array<ll, 2>;

//----------------------------------



//--------3.コード短縮化とか---------
#define rep(i,n) for(ll i = 0; i < (n); ++i)
#define rrep(i,n) for(ll i = 1; i <= (n); ++i)
#define drep(i,n) for(ll i = (n)-1; i >= 0; --i)
#define nfor(i,s,n) for(ll i=s;i<n;i++)//i=s,s+1...n-1 ノーマルfor
#define dfor(i,s,n) for(ll i = (s)-1; i>=n;i--)//s-1スタートでnまで落ちる

#define nall(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()

#define chmax(x,y) x = max(x,y)
#define chmin(x,y) x = min(x,y)

#define pb push_back
#define eb emplace_back
#define em emplace
#define pob pop_back


#define YES cout<<"Yes"<<endl
#define NO cout<<"No"<<endl
#define YN {cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}// if(a==b)YN;
#define dame cout<<-1<<endl


#define vc_unique(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define vc_rotate(v) rotate(v.begin(),v.begin()+1,v.end());

#define pop_cnt(s) ll(popcount(uint64_t(s)))

#define next_p(v) next_permutation(v.begin(),v.end())

//if (regex_match(s, regex("")))YN;//文字列sの判定を行う。コメントアウトを外して「""」の中に判定する内容を入れる

//-------------------------------




//---------4.グリッド系----------
vl di = { 0,1,0,-1 };//vl di={0,1,1,1,0,-1,-1,-1};
vl dj = { 1,0,-1,0 };//vl dj={1,1,0,-1,-1,-1,0,1};

bool out_grid(ll i, ll j, ll h, ll w) {//trueならcontinue
    return (!(0 <= i && i < h && 0 <= j && j < w));
}

#define vvl_kaiten_r(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//時計回りに90°回転
#define vvl_kaiten_l(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//反時計周りに90°回転

#define vs_kaiten_r(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//文字列版 時計回りに90°回転
#define vs_kaiten_l(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//文字列版　反時計周りに90°回転


#define vvl_tenti(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][i]=v[i][j];swap(nx,v);}
#define vs_tenti(v) {ll n = size(v); vs nx(n, string(n,'.')); rep(i, n)rep(j, n)nx[j][i] = v[i][j]; swap(nx, v);}

//--------------------------------




//-----------5.数学系--------------
#define yu_qurid(x,y) ((x)*(x)+(y)*(y))//ユークリッド距離 sqrtはしてないなので注意
#define mannhattan(x1,x2,y1,y2) (abs(x1-x2)+abs(y1-y2)) // マンハッタン距離 = |x1-x2|+|y1-y2|

template<class T>T tousa_sum1(T l, T d, T r) {//初項,公差,末項 で総和を求める
    T wide = (r - l) / d + 1;
    return (l + r) * wide / 2;
}
template<class T>T tousa_sum2(T a, T d, T n) {//初項,交差,項数 で総和を求める
    return (a * 2 + d * (n - 1)) * n / 2;
}
ll kousa_kousuu(ll l, ll r, ll d) {//初項,末項,交差 で等差数列の項数を求める
    return (r - l) / d + 1;
}
// mint touhi_sum(mint a, mint r, ll n) {//初項,公比,項数で等比数列の総和を求める
//     if (r == 1) {
//         return a * n;
//     }
//     mint bunsi = a * (r.pow(n) - mint(1));
//     mint bunbo = r - 1;
//     return bunsi / bunbo;
// }

ll nc2(ll x) { return x * (x - 1) / 2; }
ll nc3(ll x) { return x * (x - 1) * (x - 2) / 6; }

//----------------------------------------------




//-----------6.デバックや出力系------------------
void print(ld x) { printf("%.20Lf\n", x); }

void mukou_debug(vvl to, bool yukou) {//GRAPH × GRAPH用の無向グラフを出力する
    ll n = size(to); ll cnt = 0;//辺の本数
    vc<pair<ll, ll>>v; rep(i, n)for (ll t : to[i])  if (i < t || yukou)cnt++, v.eb(i + 1, t + 1);//有向グラフなら全部OK、違うなら無向なのでf<tのみ見る、using Pのやつを別のにしたいときのためにPを使わずにpair<ll,ll>にしてる
    cout << n << ' ' << cnt << endl; for (auto [f, t] : v)cout << f << ' ' << t << endl;
}

#define vc_cout(v){ll n = size(v);rep(i,n)cout<<v[i]<<endl;}//一次元配列を出力する
#define vv_cout(v){ll n = size(v);rep(i,n){rep(j,size(v[i])){cout<<v[i][j]<<' ';}cout<<endl;}}//二次元配列を出力する

struct UnionFind {
    vector<int> par, rank, siz;

    // 構造体の初期化
    UnionFind(int n) : par(n,-1), rank(n,0), siz(n,1) { }

    // 根を求める
    int root(int x) {
        if (par[x]==-1) return x; // x が根の場合は x を返す
        else return par[x] = root(par[x]); // 経路圧縮
    }

    // x と y が同じグループに属するか (= 根が一致するか)
    bool issame(int x, int y) {
        return root(x)==root(y);
    }

    // x を含むグループと y を含むグループを併合する
    bool unite(int x, int y) {
        int rx = root(x), ry = root(y); // x 側と y 側の根を取得する
        if (rx==ry) return false; // すでに同じグループのときは何もしない
        // union by rank
        if (rank[rx]<rank[ry]) swap(rx, ry); // ry 側の rank が小さくなるようにする
        par[ry] = rx; // ry を rx の子とする
        if (rank[rx]==rank[ry]) rank[rx]++; // rx 側の rank を調整する
        siz[rx] += siz[ry]; // rx 側の siz を調整する
        return true;
    }

    // x を含む根付き木のサイズを求める
    int size(int x) {
        return siz[root(x)];
    }
};

int main(){
    ll n, m, k, q;
    cin >> n >> m >> k >> q;
    vv<pll> G(n, vc<pll>());
    rep(i, m){
        ll a, b, l;
        cin >> a >> b >> l;
        a--; b--;
        G[a].pb({b, l});
        G[b].pb({a, l});
    }

    // ダイクストラで各町の祭りからの距離distを求める．
    pq_g<pll> pq;
    rep(i, k){
        ll f;
        cin >> f;
        f--;
        pq.push({0, f});
    }
    vl dist(n, INF);
    vb seen(n, false);
    while(pq.size()){
        auto [costu, u] = pq.top();
        pq.pop();

        if(seen[u]) continue;
        dist[u] = costu;
        seen[u] = true;

        for(auto [v, duv] : G[u]){
            if(seen[v]) continue;
            if(dist[v] > dist[u] + duv){
                dist[v] = dist[u] + duv;
                pq.push({dist[v], v});
            }
        }
    }

    /*  辺のコスト = (端点の祭りからの距離のうち小さい方)
        辺コストが大きいものだけでグラフFを作る
    */
    vv<pll> F(n, vc<pll>());
    UnionFind uf = UnionFind(n);
    vc<tuple<ll, ll, ll>> edges;
    rep(u, n){
        for(auto [v, l] : G[u]){
            ll c = min(dist[u], dist[v]);
            edges.push_back({u, v, c});
        }
    }

    sort(nall(edges), [](auto const &l, auto const &r){
        return get<2>(l) > get<2>(r);
    });

    for(auto [u, v, c] : edges){
        if(uf.issame(u, v)) continue;
        uf.unite(u, v);
        F[u].pb({v, c});
        F[v].pb({u, c});
    }
    
    /*  LCAの準備
        dp[i][u]: uの2^i上の親と，そのパス上での辺のコストの最小値のペア
    */
    ll maxLogN = 20;
    vv<pll> dp(maxLogN, vc<pll>(n));
    vl d(n, -1); // 根0からの距離

    auto dfs = [&](auto self, ll u, ll p, ll dd) -> void{
        if(p == -1) dp[0][u] = {p, dist[u]};
        else dp[0][u] = {p, min(dist[u], dist[p])};
        
        d[u] = dd;

        for(auto [v, c] : F[u]){
            if(v == p) continue;
            self(self, v, u, dd+1);
        }
        return;
    };

    dfs(dfs, 0, -1, 0);
    for(ll i = 0; i+1 < maxLogN; i++){
        rep(v, n){
            if(dp[i][v].first == -1) dp[i+1][v] = {-1, dp[i][v].second};
            else{
                dp[i+1][v].first = dp[i][dp[i][v].first].first;
                dp[i+1][v].second = min(dp[i][v].second, dp[i][dp[i][v].first].second);
            }
        }
    }

    /*  クエリの処理
    */
    vl ans(q);
    rep(qq, q){
        ll s, t;
        cin >> s >> t;
        s--; t--;

        ll tmpans = INF;

        if(d[s] < d[t]) swap(s, t);
        rep(i, maxLogN){
            if(((d[s] - d[t]) >> i) & 1ll){
                chmin(tmpans, dp[i][s].second);
                s = dp[i][s].first;
            }
        }

        if(s == t){
            ans[qq] = tmpans;
            continue;
        }
        drep(i, maxLogN){
            if(dp[i][s].first != dp[i][t].first){
                tmpans = min({tmpans, dp[i][s].second, dp[i][t].second});
                s = dp[i][s].first;
                t = dp[i][t].first;
            }
        }

        tmpans = min({tmpans, dp[0][s].second, dp[0][t].second});

        ans[qq] = tmpans;
    }

    for(ll a : ans) cout << a << endl;

    return 0;
}
accuracy: 1
time_ms: 320
memory_kb: 69160
final_rank: 13

code_snippet: // #ifndef ONLINE_JUDGE
// #define _GLIBCXX_DEBUG//[]で配列外参照をするとエラーにしてくれる。上下のやつがないとTLEになるので注意 ABC311Eのサンプル4みたいなデバック中のTLEは防げないので注意
// #endif

#include <bits/stdc++.h>
using namespace std;
// #include <atcoder/all>
// using namespace atcoder;


template<typename T> using vc = vector<T>;//prioriy_queueに必要なのでここにこれ書いてます
template<typename T> using vv = vc<vc<T>>;

//-------------1.型系---------------
using ll = long long;
ll INF = 2e18;

// #include <boost/multiprecision/cpp_int.hpp>//インストール的なのをしてないとできないので注意
// namespace multip = boost::multiprecision;
// //using lll = multip::cpp_int;//無制限を使いたいときはこっちを使う
// using lll = multip::int128_t;

using ld = long double;
using bl = bool;
// using mint = modint998244353;
//using mint = modint1000000007;
//using mint = modint;//使うときはコメントアウトを外す
//mint::set_mod(m);//使うときはコメントアウトを外す

template<class T> using pq = priority_queue<T, vc<T>>;//大きい順
template<class T> using pq_g = priority_queue<T, vc<T>, greater<T>>;//小さい順
//-----------------------------------



//-------------2.配列系--------------
using vl = vc<ll>; using vvl = vv<ll>; using vvvl = vv<vl>; using vvvvl = vv<vvl>;
using vs = vc<string>; using vvs = vv<string>;
using vb = vc<bl>; using vvb = vv<bl>; using vvvb = vv<vb>;
using vld = vc<ld>; using vvld = vv<ld>; using vvvld = vv<vld>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
// using vmint = vc<mint>; using vvmint = vv<mint>; using vvvmint = vv<vmint>;

//配列外参照対策のやつは一番上の行にあります

#define rep(i,n) for(ll i = 0; i < (n); ++i)//↓でrepを使うので書いてます
template<class T>istream& operator>>(istream& i, vc<T>& v) { rep(j, size(v))i >> v[j]; return i; }

using ar2 = array<ll, 2>;

//----------------------------------



//--------3.コード短縮化とか---------
#define rep(i,n) for(ll i = 0; i < (n); ++i)
#define rrep(i,n) for(ll i = 1; i <= (n); ++i)
#define drep(i,n) for(ll i = (n)-1; i >= 0; --i)
#define nfor(i,s,n) for(ll i=s;i<n;i++)//i=s,s+1...n-1 ノーマルfor
#define dfor(i,s,n) for(ll i = (s)-1; i>=n;i--)//s-1スタートでnまで落ちる

#define nall(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()

#define chmax(x,y) x = max(x,y)
#define chmin(x,y) x = min(x,y)

#define pb push_back
#define eb emplace_back
#define em emplace
#define pob pop_back


#define YES cout<<"Yes"<<endl
#define NO cout<<"No"<<endl
#define YN {cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}// if(a==b)YN;
#define dame cout<<-1<<endl


#define vc_unique(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define vc_rotate(v) rotate(v.begin(),v.begin()+1,v.end());

#define pop_cnt(s) ll(popcount(uint64_t(s)))

#define next_p(v) next_permutation(v.begin(),v.end())

//if (regex_match(s, regex("")))YN;//文字列sの判定を行う。コメントアウトを外して「""」の中に判定する内容を入れる

//-------------------------------




//---------4.グリッド系----------
vl di = { 0,1,0,-1 };//vl di={0,1,1,1,0,-1,-1,-1};
vl dj = { 1,0,-1,0 };//vl dj={1,1,0,-1,-1,-1,0,1};

bool out_grid(ll i, ll j, ll h, ll w) {//trueならcontinue
    return (!(0 <= i && i < h && 0 <= j && j < w));
}

#define vvl_kaiten_r(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//時計回りに90°回転
#define vvl_kaiten_l(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//反時計周りに90°回転

#define vs_kaiten_r(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//文字列版 時計回りに90°回転
#define vs_kaiten_l(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//文字列版　反時計周りに90°回転


#define vvl_tenti(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][i]=v[i][j];swap(nx,v);}
#define vs_tenti(v) {ll n = size(v); vs nx(n, string(n,'.')); rep(i, n)rep(j, n)nx[j][i] = v[i][j]; swap(nx, v);}

//--------------------------------




//-----------5.数学系--------------
#define yu_qurid(x,y) ((x)*(x)+(y)*(y))//ユークリッド距離 sqrtはしてないなので注意
#define mannhattan(x1,x2,y1,y2) (abs(x1-x2)+abs(y1-y2)) // マンハッタン距離 = |x1-x2|+|y1-y2|

template<class T>T tousa_sum1(T l, T d, T r) {//初項,公差,末項 で総和を求める
    T wide = (r - l) / d + 1;
    return (l + r) * wide / 2;
}
template<class T>T tousa_sum2(T a, T d, T n) {//初項,交差,項数 で総和を求める
    return (a * 2 + d * (n - 1)) * n / 2;
}
ll kousa_kousuu(ll l, ll r, ll d) {//初項,末項,交差 で等差数列の項数を求める
    return (r - l) / d + 1;
}
// mint touhi_sum(mint a, mint r, ll n) {//初項,公比,項数で等比数列の総和を求める
//     if (r == 1) {
//         return a * n;
//     }
//     mint bunsi = a * (r.pow(n) - mint(1));
//     mint bunbo = r - 1;
//     return bunsi / bunbo;
// }

ll nc2(ll x) { return x * (x - 1) / 2; }
ll nc3(ll x) { return x * (x - 1) * (x - 2) / 6; }

//----------------------------------------------




//-----------6.デバックや出力系------------------
void print(ld x) { printf("%.20Lf\n", x); }

void mukou_debug(vvl to, bool yukou) {//GRAPH × GRAPH用の無向グラフを出力する
    ll n = size(to); ll cnt = 0;//辺の本数
    vc<pair<ll, ll>>v; rep(i, n)for (ll t : to[i])  if (i < t || yukou)cnt++, v.eb(i + 1, t + 1);//有向グラフなら全部OK、違うなら無向なのでf<tのみ見る、using Pのやつを別のにしたいときのためにPを使わずにpair<ll,ll>にしてる
    cout << n << ' ' << cnt << endl; for (auto [f, t] : v)cout << f << ' ' << t << endl;
}

#define vc_cout(v){ll n = size(v);rep(i,n)cout<<v[i]<<endl;}//一次元配列を出力する
#define vv_cout(v){ll n = size(v);rep(i,n){rep(j,size(v[i])){cout<<v[i][j]<<' ';}cout<<endl;}}//二次元配列を出力する

struct UnionFind {
    vector<int> par, rank, siz;

    // 構造体の初期化
    UnionFind(int n) : par(n,-1), rank(n,0), siz(n,1) { }

    // 根を求める
    int root(int x) {
        if (par[x]==-1) return x; // x が根の場合は x を返す
        else return par[x] = root(par[x]); // 経路圧縮
    }

    // x と y が同じグループに属するか (= 根が一致するか)
    bool issame(int x, int y) {
        return root(x)==root(y);
    }

    // x を含むグループと y を含むグループを併合する
    bool unite(int x, int y) {
        int rx = root(x), ry = root(y); // x 側と y 側の根を取得する
        if (rx==ry) return false; // すでに同じグループのときは何もしない
        // union by rank
        if (rank[rx]<rank[ry]) swap(rx, ry); // ry 側の rank が小さくなるようにする
        par[ry] = rx; // ry を rx の子とする
        if (rank[rx]==rank[ry]) rank[rx]++; // rx 側の rank を調整する
        siz[rx] += siz[ry]; // rx 側の siz を調整する
        return true;
    }

    // x を含む根付き木のサイズを求める
    int size(int x) {
        return siz[root(x)];
    }
};

int main(){
    ll n, m, k, q;
    cin >> n >> m >> k >> q;
    vv<pll> G(n, vc<pll>());
    rep(i, m){
        ll a, b, l;
        cin >> a >> b >> l;
        a--; b--;
        G[a].pb({b, l});
        G[b].pb({a, l});
    }

    // ダイクストラで各町の祭りからの距離distを求める．
    pq_g<pll> pq;
    rep(i, k){
        ll f;
        cin >> f;
        f--;
        pq.push({0, f});
    }
    vl dist(n, INF);
    vb seen(n, false);
    while(pq.size()){
        auto [costu, u] = pq.top();
        pq.pop();

        if(seen[u]) continue;
        dist[u] = costu;
        seen[u] = true;

        for(auto [v, duv] : G[u]){
            if(seen[v]) continue;
            if(dist[v] > dist[u] + duv){
                dist[v] = dist[u] + duv;
                pq.push({dist[v], v});
            }
        }
    }

    /*  辺のコスト = (端点の祭りからの距離のうち小さい方)
        辺コストが大きいものだけでグラフFを作る
    */
    vv<pll> F(n, vc<pll>());
    UnionFind uf = UnionFind(n);
    vc<tuple<ll, ll, ll>> edges;
    rep(u, n){
        for(auto [v, l] : G[u]){
            ll c = min(dist[u], dist[v]);
            edges.push_back({u, v, c});
        }
    }

    sort(nall(edges), [](auto const &l, auto const &r){
        return get<2>(l) > get<2>(r);
    });

    for(auto [u, v, c] : edges){
        if(uf.issame(u, v)) continue;
        uf.unite(u, v);
        F[u].pb({v, c});
        F[v].pb({u, c});
    }
    
    /*  LCAの準備
        dp[i][u]: uの2^i上の親と，そのパス上での辺のコストの最小値のペア
    */
    ll maxLogN = 20;
    vv<pll> dp(maxLogN, vc<pll>(n));
    vl d(n, -1); // 根0からの距離

    auto dfs = [&](auto self, ll u, ll p, ll dd) -> void{
        if(p == -1) dp[0][u] = {p, dist[u]};
        else dp[0][u] = {p, min(dist[u], dist[p])};
        
        d[u] = dd;

        for(auto [v, c] : F[u]){
            if(v == p) continue;
            self(self, v, u, dd+1);
        }
        return;
    };

    dfs(dfs, 0, -1, 0);
    for(ll i = 0; i+1 < maxLogN; i++){
        rep(v, n){
            if(dp[i][v].first == -1) dp[i+1][v] = {-1, dp[i][v].second};
            else{
                dp[i+1][v].first = dp[i][dp[i][v].first].first;
                dp[i+1][v].second = min(dp[i][v].second, dp[i][dp[i][v].first].second);
            }
        }
    }

    /*  クエリの処理
    */
    vl ans(q);
    rep(qq, q){
        ll s, t;
        cin >> s >> t;
        s--; t--;

        ll tmpans = INF;

        if(d[s] < d[t]) swap(s, t);
        rep(i, maxLogN){
            if(((d[s] - d[t]) >> i) & 1ll){
                chmin(tmpans, dp[i][s].second);
                s = dp[i][s].first;
            }
        }

        if(s == t){
            ans[qq] = tmpans;
            continue;
        }
        drep(i, maxLogN){
            if(dp[i][s].first != dp[i][t].first){
                tmpans = min({tmpans, dp[i][s].second, dp[i][t].second});
                s = dp[i][s].first;
                t = dp[i][t].first;
            }
        }

        ans[qq] = tmpans;
    }

    for(ll a : ans) cout << a << endl;

    return 0;
}
accuracy: 0.12903225806451613
time_ms: 180
memory_kb: 67740
final_rank: 19

code_snippet: // #ifndef ONLINE_JUDGE
// #define _GLIBCXX_DEBUG//[]で配列外参照をするとエラーにしてくれる。上下のやつがないとTLEになるので注意 ABC311Eのサンプル4みたいなデバック中のTLEは防げないので注意
// #endif

#include <bits/stdc++.h>
using namespace std;
// #include <atcoder/all>
// using namespace atcoder;


template<typename T> using vc = vector<T>;//prioriy_queueに必要なのでここにこれ書いてます
template<typename T> using vv = vc<vc<T>>;

//-------------1.型系---------------
using ll = long long;
ll INF = 2e18;

// #include <boost/multiprecision/cpp_int.hpp>//インストール的なのをしてないとできないので注意
// namespace multip = boost::multiprecision;
// //using lll = multip::cpp_int;//無制限を使いたいときはこっちを使う
// using lll = multip::int128_t;

using ld = long double;
using bl = bool;
// using mint = modint998244353;
//using mint = modint1000000007;
//using mint = modint;//使うときはコメントアウトを外す
//mint::set_mod(m);//使うときはコメントアウトを外す

template<class T> using pq = priority_queue<T, vc<T>>;//大きい順
template<class T> using pq_g = priority_queue<T, vc<T>, greater<T>>;//小さい順
//-----------------------------------



//-------------2.配列系--------------
using vl = vc<ll>; using vvl = vv<ll>; using vvvl = vv<vl>; using vvvvl = vv<vvl>;
using vs = vc<string>; using vvs = vv<string>;
using vb = vc<bl>; using vvb = vv<bl>; using vvvb = vv<vb>;
using vld = vc<ld>; using vvld = vv<ld>; using vvvld = vv<vld>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
// using vmint = vc<mint>; using vvmint = vv<mint>; using vvvmint = vv<vmint>;

//配列外参照対策のやつは一番上の行にあります

#define rep(i,n) for(ll i = 0; i < (n); ++i)//↓でrepを使うので書いてます
template<class T>istream& operator>>(istream& i, vc<T>& v) { rep(j, size(v))i >> v[j]; return i; }

using ar2 = array<ll, 2>;

//----------------------------------



//--------3.コード短縮化とか---------
#define rep(i,n) for(ll i = 0; i < (n); ++i)
#define rrep(i,n) for(ll i = 1; i <= (n); ++i)
#define drep(i,n) for(ll i = (n)-1; i >= 0; --i)
#define nfor(i,s,n) for(ll i=s;i<n;i++)//i=s,s+1...n-1 ノーマルfor
#define dfor(i,s,n) for(ll i = (s)-1; i>=n;i--)//s-1スタートでnまで落ちる

#define nall(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()

#define chmax(x,y) x = max(x,y)
#define chmin(x,y) x = min(x,y)

#define pb push_back
#define eb emplace_back
#define em emplace
#define pob pop_back


#define YES cout<<"Yes"<<endl
#define NO cout<<"No"<<endl
#define YN {cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}// if(a==b)YN;
#define dame cout<<-1<<endl


#define vc_unique(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define vc_rotate(v) rotate(v.begin(),v.begin()+1,v.end());

#define pop_cnt(s) ll(popcount(uint64_t(s)))

#define next_p(v) next_permutation(v.begin(),v.end())

//if (regex_match(s, regex("")))YN;//文字列sの判定を行う。コメントアウトを外して「""」の中に判定する内容を入れる

//-------------------------------




//---------4.グリッド系----------
vl di = { 0,1,0,-1 };//vl di={0,1,1,1,0,-1,-1,-1};
vl dj = { 1,0,-1,0 };//vl dj={1,1,0,-1,-1,-1,0,1};

bool out_grid(ll i, ll j, ll h, ll w) {//trueならcontinue
    return (!(0 <= i && i < h && 0 <= j && j < w));
}

#define vvl_kaiten_r(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//時計回りに90°回転
#define vvl_kaiten_l(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//反時計周りに90°回転

#define vs_kaiten_r(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//文字列版 時計回りに90°回転
#define vs_kaiten_l(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//文字列版　反時計周りに90°回転


#define vvl_tenti(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][i]=v[i][j];swap(nx,v);}
#define vs_tenti(v) {ll n = size(v); vs nx(n, string(n,'.')); rep(i, n)rep(j, n)nx[j][i] = v[i][j]; swap(nx, v);}

//--------------------------------




//-----------5.数学系--------------
#define yu_qurid(x,y) ((x)*(x)+(y)*(y))//ユークリッド距離 sqrtはしてないなので注意
#define mannhattan(x1,x2,y1,y2) (abs(x1-x2)+abs(y1-y2)) // マンハッタン距離 = |x1-x2|+|y1-y2|

template<class T>T tousa_sum1(T l, T d, T r) {//初項,公差,末項 で総和を求める
    T wide = (r - l) / d + 1;
    return (l + r) * wide / 2;
}
template<class T>T tousa_sum2(T a, T d, T n) {//初項,交差,項数 で総和を求める
    return (a * 2 + d * (n - 1)) * n / 2;
}
ll kousa_kousuu(ll l, ll r, ll d) {//初項,末項,交差 で等差数列の項数を求める
    return (r - l) / d + 1;
}
// mint touhi_sum(mint a, mint r, ll n) {//初項,公比,項数で等比数列の総和を求める
//     if (r == 1) {
//         return a * n;
//     }
//     mint bunsi = a * (r.pow(n) - mint(1));
//     mint bunbo = r - 1;
//     return bunsi / bunbo;
// }

ll nc2(ll x) { return x * (x - 1) / 2; }
ll nc3(ll x) { return x * (x - 1) * (x - 2) / 6; }

//----------------------------------------------




//-----------6.デバックや出力系------------------
void print(ld x) { printf("%.20Lf\n", x); }

void mukou_debug(vvl to, bool yukou) {//GRAPH × GRAPH用の無向グラフを出力する
    ll n = size(to); ll cnt = 0;//辺の本数
    vc<pair<ll, ll>>v; rep(i, n)for (ll t : to[i])  if (i < t || yukou)cnt++, v.eb(i + 1, t + 1);//有向グラフなら全部OK、違うなら無向なのでf<tのみ見る、using Pのやつを別のにしたいときのためにPを使わずにpair<ll,ll>にしてる
    cout << n << ' ' << cnt << endl; for (auto [f, t] : v)cout << f << ' ' << t << endl;
}

#define vc_cout(v){ll n = size(v);rep(i,n)cout<<v[i]<<endl;}//一次元配列を出力する
#define vv_cout(v){ll n = size(v);rep(i,n){rep(j,size(v[i])){cout<<v[i][j]<<' ';}cout<<endl;}}//二次元配列を出力する

/*  とりあえず各町のお祭りからの距離を知りたいので、多始点ダイクストラみたいなことをする
    ダブリングらしいけどどうテーブルを作るか
    答えで二分探索したい気持ち
    dp[i][j]: 街jからお祭りまでの距離を2^i以下で移動できる街のリスト？
    Q≤1e5なので各クエリにO(log?)くらいでこたえたい

*/

struct UnionFind {
    vector<int> par, rank, siz;

    // 構造体の初期化
    UnionFind(int n) : par(n,-1), rank(n,0), siz(n,1) { }

    // 根を求める
    int root(int x) {
        if (par[x]==-1) return x; // x が根の場合は x を返す
        else return par[x] = root(par[x]); // 経路圧縮
    }

    // x と y が同じグループに属するか (= 根が一致するか)
    bool issame(int x, int y) {
        return root(x)==root(y);
    }

    // x を含むグループと y を含むグループを併合する
    bool unite(int x, int y) {
        int rx = root(x), ry = root(y); // x 側と y 側の根を取得する
        if (rx==ry) return false; // すでに同じグループのときは何もしない
        // union by rank
        if (rank[rx]<rank[ry]) swap(rx, ry); // ry 側の rank が小さくなるようにする
        par[ry] = rx; // ry を rx の子とする
        if (rank[rx]==rank[ry]) rank[rx]++; // rx 側の rank を調整する
        siz[rx] += siz[ry]; // rx 側の siz を調整する
        return true;
    }

    // x を含む根付き木のサイズを求める
    int size(int x) {
        return siz[root(x)];
    }
};

int main(){
    ll n, m, k, q;
    cin >> n >> m >> k >> q;
    vv<pll> G(n, vc<pll>());
    rep(i, m){
        ll a, b, l;
        cin >> a >> b >> l;
        a--; b--;
        G[a].pb(make_pair(b, l));
        G[b].pb(make_pair(a, l));
    }
    set<ll> st;
    rep(i, k){
        ll f;
        cin >> f;
        f--;
        st.insert(f);
    }
    vl s(q), t(q);
    rep(i, q){
        cin >> s[i] >> t[i];
        s[i]--; t[i]--;
    }

    // 各街のお祭りまでの距離を求める
    vl d(n, INF); // d[i]: 町iのお祭りまでの距離
    pq_g<pll> pq;
    for(ll u : st) pq.push(make_pair(0, u));
    vb seen(n, false);

    while(pq.size()){
        auto [costu, u] = pq.top();
        pq.pop();

        if(costu > d[u]) continue;
        d[u] = costu;
        seen[u] = true;

        for(auto [v, duv] : G[u]){
            if(seen[v]) continue;
            if(d[v] > d[u] + duv){
                d[v] = d[u] + duv;
                pq.push(make_pair(d[v], v));
            }
        }
    }

    // 辺をコストの降順でソート。コストが最大になる全域木Fをつくる。クラスカル法
    vc<tuple<ll, ll, ll>> edges;
    rep(u, n){
        for(auto [v, l] : G[u]){
            edges.pb(make_tuple(u, v, min(d[u], d[v])));
        }
    }

    sort(nall(edges), [](auto const &l, auto const &r){
        return get<2>(l) > get<2>(r);
    });

    vvl F(n, vl());
    UnionFind uf = UnionFind(n);
    for(auto [from, to, cost] : edges){
        if(!uf.issame(from, to)){
            uf.unite(from, to);
            F[from].pb(to);
            F[to].pb(from);
        }
    }

    ll maxLogN = 20;
    /*  dp[i][u]: Fを頂点0を根とする根付き木としてみたときの
        頂点uの2^i個上の親と,そのパス上でのコスト最小の頂点のペア
    */
    vv<pll> dp(maxLogN, vc<pll>(n));
    vl dist(n, -1);

    auto dfs = [&](auto self, ll u, ll p, ll dd) -> void{
        ll second = (p == -1 ? d[u] : min(d[p], d[u]));
        dp[0][u] = make_pair(p, second);
        dist[u] = dd;
        for(ll v : F[u]){
            if(v == p) continue;

            self(self, v, u, dd+1);
        }
        return;
    };

    dfs(dfs, 0, -1, 0);
    rep(i, maxLogN-1){
        rep(j, n){
            if(dp[i][j].first == -1){
                dp[i+1][j] = make_pair(-1, dp[i][j].second);
            }
            else{
                dp[i+1][j] = make_pair(dp[i][dp[i][j].first].first, min(dp[i][j].second, dp[i][dp[i][j].first].second));
            }
        }
    }

    rep(qq, q){
        ll ans = INF;

        ll u = s[qq], v = t[qq];
        if(dist[u] < dist[v]) swap(u, v);
        rep(i, maxLogN){
            if(((dist[u] - dist[v]) >> i) & 1ll){
                chmin(ans, dp[i][u].second); // uを変更する前にUが上に行くパス上の最小値を調べる必要がある
                u = dp[i][u].first;
            }
        }

        if(u == v){
            cout << ans << endl;
            continue;    
        }
        drep(i, maxLogN){
            if(dp[i][u].first != dp[i][v].first){
                ans = min({ans, dp[i][u].second, dp[i][v].second}); // u, vを変更する前にu,vそれぞれが通ることになるパスを調べておく。
                u = dp[i][u].first;
                v = dp[i][v].first;
            }
        }
        ans = min(ans, dp[0][u].second);

        cout << ans << endl;
    }

    // vv_cout(F);
    // cout << endl;
    // vc_cout(dist);

    return 0;
}
accuracy: 0.12903225806451613
time_ms: 170
memory_kb: 66728
final_rank: 18

code_snippet: ///***LTT***///
/// ->NHAT QUOC GIA<- ///
#include<bits/stdc++.h>
//#pragma GCC optimize ("O3")
//#pragma GCC optimize ("unroll-loops")
//#pragma GCC target("popcnt")
//#define int long long
#define endl "\n"
#define F first
#define S second
#define pb push_back
using namespace std;
vector <int> lg2;
//void MAKE_LOG_ARR(int n){
//    lg2.resize(n + 3);
//    for (int i = 1;i <= n;++i){
//        lg2[i] = __lg(i);
//    }
//}
const long long oo = 1e9+7;
const int N = 1e5 + 10;
int n, m, k, q;
int qu[N], qv[N];
long long dis[N];
vector <pair <int, int>> adj[N];
int parent[N], sz[N];
int b[N];
priority_queue <pair <long long, int>> pq;
int L[N], R[N];
long long ans[N];
vector <int> in[N];
bool cmp(int a, int b){
    return dis[a] > dis[b];
}
int finds(int u){
    if (u == parent[u]) return u;
    int pu = finds(parent[u]);
    parent[u] = pu;
    return pu;
}
void unions(int u, int v){
    u = finds(u);
    v = finds(v);
    if (u != v){
        if (sz[u] > sz[v]) swap(u,v);
        parent[v] = u;
        sz[u] += sz[v];
    }
    return;
}
void solve(){
    cin >> n >> m >> k >> q;
    for (int i = 1;i <= m;++i){
        int u, v, w;
        cin >> u >> v >> w;
        adj[u].pb({v,w});
        adj[v].pb({u,w});
    }
    for (int i = 1;i <= n;++i){
        dis[i] = oo * oo;
    }
    for (int i = 1;i <= k;++i){
        int x;
        cin >> x;
        dis[x] = 0;
        pq.push({0, x});
    }
    while (!pq.empty()){
        int u = pq.top().S;
        long long d =- pq.top().F;
        pq.pop();
        if (d > dis[u]) continue;
        for (int i = 0;i < adj[u].size();++i){
            int v = adj[u][i].F;
            int w = adj[u][i].S;
            if (d + w < dis[v]){
                dis[v] = d + w;
                pq.push({-dis[v], v});
            }
        }
    }
    for (int i = 1;i <= q;++i){
        cin >> qu[i] >> qv[i];
    }
    for (int i = 1;i <= n;++i){
        b[i] = i;
    }
    sort(b + 1,b + n + 1, cmp);
//    for (int i = 1;i <= n;++i){
//        cout << b[i] <<" "<< dis[b[i]] <<"\n";
//    }
    for (int i = 1;i <= q;++i){
        ans[i] = 0;
        L[i] = 1;
        R[i] = n;
    }
    while (1){
        bool found = false;
        for (int i = 1;i <= q;++i){
            if (L[i] <= R[i]){
                int mid = (L[i] + R[i])/2;
                in[mid].pb(i);

                found = true;
            }
        }
        if (!found){
            break;
        }
        for (int i = 1;i <= n;++i){
            parent[i] = i;
            sz[i] = 1;
        }
        for (int i = 1;i <= n;++i){
            for (int j = 0;j < adj[b[i]].size();++j){
                int v = adj[b[i]][j].F;
                if (dis[v] >= dis[b[i]])
                unions(b[i], v);
            }
            for (int j : in[i]){
                if (finds(qu[j]) == finds(qv[j])){
                    R[j] = i - 1;
                    ans[j] = dis[b[i]];
                } else L[j] = i + 1;
            }
            in[i].clear();
        }
    }
    for (int i = 1;i <= q;++i){
        cout << ans[i] <<"\n";
    }
    return;
}
int main(){
    ios_base::sync_with_stdio(NULL);
    cin.tie(NULL);
    cout.tie(NULL);
    if (fopen("ltt.inp", "r")){
        freopen("ltt.inp", "r", stdin);
        freopen("ltt.out", "w", stdout);
    }
//    int t;
//    cin >> t;
//    while(t--){
    solve();
//    }
    return 0;
}
accuracy: 1
time_ms: 350
memory_kb: 28448
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 1e9 + 10;
const ll INFL = 4e18;

template <typename T, T (*op)(T, T), T (*e)()>
struct LcaVertexQuery {
    LcaVertexQuery(const vector<vector<int>> &g, const vector<T> &v, int root) {
        int n = g.size();
        int k = 1;
        while ((1 << k) < n) k++;
        parent = vector<vector<int>>(k, vector<int>(n, -1));
        dep = vector<int>(n);
        dat = vector<vector<T>>(k, vector<T>(n, e()));
        dfs(g, v, root, -1, 0);
        for (int i = 0; i + 1 < k; i++) {
            for (int j = 0; j < n; j++) {
                parent[i + 1][j] = parent[i][j] == -1 ? -1 : parent[i][parent[i][j]];
                dat[i + 1][j] = parent[i][j] == -1 ? e() : op(dat[i][j], dat[i][parent[i][j]]);
            }
        }
    }
    T query(int u, int v) {
        T ret = e();
        if (dep[u] > dep[v]) swap(u, v);
        while (dep[u] < dep[v]) {
            int k = 0;
            while (dep[v] - dep[u] >= (1 << (k + 1))) k++;
            ret = op(ret, dat[k][v]);
            v = parent[k][v];
        }
        while (parent[0][u] != parent[0][v]) {
            int k = 0;
            while (parent[k + 1][u] != parent[k + 1][v]) k++;
            ret = op(ret, dat[k][u]);
            ret = op(ret, dat[k][v]);
            u = parent[k][u];
            v = parent[k][v];
        }
        ret = op(ret, dat[0][u]);
        ret = op(ret, dat[0][v]);
        return ret;
    }

private:
    vector<vector<int>> parent;
    vector<int> dep;
    vector<vector<T>> dat;
    void dfs(const vector<vector<int>> &g, const vector<T> &v, int now, int par, int d) {
        dat[0][now] = v[now];
        parent[0][now] = par;
        dep[now] = d;
        for (int nxt : g[now]) {
            if (nxt == par) continue;
            dfs(g, v, nxt, now, d + 1);
        }
    }
};

struct DSU {
    DSU() = default;
    DSU(int n) {
        par = vector<int>(n);
        sz = vector<int>(n);
        for (int i = 0; i < n; i++) {
            par[i] = i;
            sz[i] = 1;
        }
        forest_count = n;
    }
    int find(int x) {
        if (par[x] == x) {
            return x;
        }
        par[x] = find(par[x]);
        return par[x];
    }
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return;
        }
        if (sz[x] < sz[y]) {
            swap(x, y);
        }
        par[y] = x;
        sz[x] += sz[y];
        forest_count--;
    }
    int size(int x) {
        return sz[find(x)];
    }
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    int count() {
        return forest_count;
    }
    vector<vector<int>> groups() {
        int n = par.size();
        vector<vector<int>> res(n);
        for (int i = 0; i < n; i++) {
            res[find(i)].push_back(i);
        }
        res.erase(remove_if(res.begin(), res.end(), [&](const vector<int> &v) { return v.empty(); }), res.end());
        return res;
    }

private:
    vector<int> par, sz;
    int forest_count;
};

ll op(ll a, ll b) {
    return min(a, b);
}
ll e() {
    return INFL;
}

int main() {
    int N, M, K, Q;
    cin >> N >> M >> K >> Q;
    vector<vector<pair<int, ll>>> G(N);
    for (int i = 0; i < M; i++) {
        int a, b;
        ll c;
        cin >> a >> b >> c;
        a--;
        b--;
        G[a].push_back({b, c});
        G[b].push_back({a, c});
    }

    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
    vector<ll> dist(N, INFL);
    for (int i = 0; i < K; i++) {
        int a;
        cin >> a;
        a--;
        dist[a] = 0;
        pq.push({0, a});
    }

    while (!pq.empty()) {
        auto [d, now] = pq.top();
        pq.pop();
        if (dist[now] < d) continue;
        for (auto [nxt, cost] : G[now]) {
            if (dist[nxt] > dist[now] + cost) {
                dist[nxt] = dist[now] + cost;
                pq.push({dist[nxt], nxt});
            }
        }
    }

    vector<tuple<ll, int, int>> edges;
    for (int i = 0; i < N; i++) {
        for (auto [nxt, cost] : G[i]) {
            if (dist[i] < dist[nxt]) {
                edges.push_back({dist[i], i, nxt});
            }
        }
    }
    sort(edges.begin(), edges.end(), greater{});

    vector<vector<int>> G2(N);

    DSU dsu(N);
    for (auto [d, a, b] : edges) {
        if (dsu.same(a, b)) continue;
        dsu.unite(a, b);
        G2[a].push_back(b);
        G2[b].push_back(a);
    }

    LcaVertexQuery<ll, op, e> lca(G2, dist, 0);

    for (int i = 0; i < Q; i++) {
        int a, b;
        cin >> a >> b;
        a--;
        b--;
        cout << lca.query(a, b) << '\n';
    }
}
accuracy: 0.12903225806451613
time_ms: 160
memory_kb: 47008
final_rank: 17

code_snippet: #include <bits/stdc++.h>




namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa


namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa



namespace zawa {

class DisjointSetUnion {
public:
    DisjointSetUnion() = default;

    DisjointSetUnion(usize n) : n_{n}, data_(n, -1) {
        data_.shrink_to_fit();
    }
    
    u32 leader(u32 v) {
        return data_[v] < 0 ? v : static_cast<u32>(data_[v] = leader(data_[v]));
    }

    bool same(u32 u, u32 v) {
        return leader(u) == leader(v);
    }

    bool merge(u32 u, u32 v) {
        assert(u < n_);
        assert(v < n_);
        u = leader(u);
        v = leader(v);
        if (u == v) return false;
        if (data_[u] > data_[v]) std::swap(u, v);
        data_[u] += data_[v];
        data_[v] = u;
        return true;
    }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(u32 v) {
        assert(v < n_);
        return static_cast<usize>(-data_[leader(v)]);
    }

    std::vector<std::vector<u32>> enumerate() {
        std::vector<std::vector<u32>> res(n_);
        for (u32 v{} ; v < n_ ; v++) {
            res[leader(v)].push_back(v);
        }
        res.erase(std::remove_if(res.begin(), res.end(),
                    [](const auto& arr) -> bool { return arr.empty(); }), res.end());
        return res;
    }

private:
    usize n_{};
    std::vector<i32> data_;
};

} // namespace zawa
using namespace zawa;

int main() {
    SetFastIO();

    int N, M, K, Q;
    std::cin >> N >> M >> K >> Q;
    std::vector<std::vector<std::pair<int, int>>> g(N);
    for (int i{} ; i < M ; i++) {
        int A, B, L;
        std::cin >> A >> B >> L;
        A--; B--;
        g[A].push_back(std::pair{ B, L });
        g[B].push_back(std::pair{ A, L });
    }
    const long long INF{(long long)1e12};
    std::vector<long long> dist(N, INF);
    using qt = std::pair<long long, int>;
    std::priority_queue<qt, std::vector<qt>, std::greater<qt>> que;
    for (int i{} ; i < K ; i++) {
        int F;
        std::cin >> F;
        F--;
        dist[F] = 0;
        que.push(qt{ dist[F], F });
    }
    while (que.size()) {
        auto [d, v]{que.top()};
        que.pop();
        if (dist[v] < d) continue;
        for (auto [x, w] : g[v]) if (dist[x] > d + w) {
            dist[x] = d + w;
            que.push(qt{ dist[x], x });
        }
    } 
    std::vector<int> S(Q), T(Q);
    for (int i{} ; i < Q ; i++) {
        std::cin >> S[i] >> T[i];
        S[i]--; T[i]--;
    }
    std::vector<long long> ok(N), ng(N, INF);
    while (true) {
        bool brk{true};
        std::vector<std::pair<long long, int>> ord;
        ord.reserve(N + Q);
        for (int i{} ; i < Q ; i++) if (ng[i] - ok[i] > 1) {
            brk = false;
            long long mid{(ok[i] + ng[i]) / 2};
            ord.push_back(std::pair{ mid, i });
        }
        if (brk) break;
        for (int i{} ; i < N ; i++) {
            ord.push_back(std::pair{ dist[i], Q + i });
        }
        std::sort(ord.begin(), ord.end());
        std::reverse(ord.begin(), ord.end());
        DisjointSetUnion dsu(N);
        std::vector<bool> use(N);
        for (auto [d, t] : ord) {
            if (t >= Q) {
                t -= Q;
                assert(!use[t]);
                use[t] = true;
                for (auto [x, _] : g[t]) if (use[x]) {
                    dsu.merge(t, x);
                }
            }
            else {
                if (dsu.same(S[t], T[t])) ok[t] = d;
                else ng[t] = d;
            }
        }
    }
    for (int i{} ; i < Q ; i++) {
        std::cout << ok[i] << '\n';
    } 
}
accuracy: 1
time_ms: 870
memory_kb: 19060
final_rank: 12

code_snippet: #include <stdio.h>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

const int MAX_N = 100000;
const int INF = 99999999;
const int SEG_SIZE = 524288;

// Union-Find用の配列
int UF[MAX_N];
// 各頂点までの最短距離
int ijk[MAX_N];
// 訪問済みフラグ
bool v[MAX_N];
// グラフの隣接リスト表現
vector<pair<int, int>> g[MAX_N];
// エッジリスト
pair<int, pair<int, int>> edge[200000];
// 各頂点の子を管理するリスト
vector<int> t[MAX_N];
// セグメントツリー
int segtree[SEG_SIZE];

// 区間の最小値をクエリで取得する関数
int query(int a, int b, int c, int d, int e) {
    if (d < a || b < c) return INF;
    if (c <= a && b <= d) return segtree[e];
    return min(query(a, (a + b) / 2, c, d, e * 2), query((a + b) / 2 + 1, b, c, d, e * 2 + 1));
}

// セグメントツリーの値を更新する関数
void set(int a, int b) {
    a += SEG_SIZE / 2;
    while (a) {
        segtree[a] = min(segtree[a], b);
        a /= 2;
    }
}

// Union-FindのFIND関数
int FIND(int a) {
    if (UF[a] < 0) return a;
    return UF[a] = FIND(UF[a]);
}

// Union-FindのUNION関数
void UNION(int a, int b) {
    a = FIND(a); b = FIND(b);
    if (a == b) return;
    UF[a] += UF[b];
    UF[b] = a;
    t[a].push_back(b);
}

// DFS用の変数
int now = 0;
int F[MAX_N];  // 各頂点のDFSで訪れる順序
int TO[MAX_N]; // DFSで訪れた頂点の順序を格納
int num = 0;

// DFS関数
void dfs(int a) {
    F[a] = now;
    int K = num++;
    TO[K] = a;
    set(now++, K);
    for (int i : t[a]) {
        dfs(i);
        set(now++, K);
    }
}

int main() {
    int a, b, c, d;
    scanf("%d%d%d%d", &a, &b, &c, &d);

    // グラフの入力を受け取る
    for (int i = 0; i < b; i++) {
        int p, q, r;
        scanf("%d%d%d", &p, &q, &r);
        p--; q--;  // 0-indexed に変換
        g[p].push_back(make_pair(q, r));
        g[q].push_back(make_pair(p, r));
        edge[i] = make_pair(0, make_pair(p, q));
    }

    // 初期化
    fill(ijk, ijk + a, INF);
    fill(UF, UF + a, -1);
    fill(segtree, segtree + SEG_SIZE, INF);

    // 最短距離計算用の優先度付きキュー
    priority_queue<pair<int, int>> Q;
    for (int i = 0; i < c; i++) {
        int e;
        scanf("%d", &e);
        e--;  // 0-indexed に変換
        ijk[e] = 0;
        Q.push(make_pair(0, e));
    }

    // ダイクストラ法による最短距離計算
    while (!Q.empty()) {
        int cost = -Q.top().first;
        int at = Q.top().second;
        Q.pop();
        if (v[at]) continue;
        v[at] = true;
        ijk[at] = cost;
        for (auto& edge : g[at]) {
            if (!v[edge.first] && ijk[edge.first] > ijk[at] + edge.second) {
                ijk[edge.first] = ijk[at] + edge.second;
                Q.push(make_pair(-ijk[edge.first], edge.first));
            }
        }
    }

    // エッジを最短距離に基づいて再構成
    for (int i = 0; i < b; i++) {
        edge[i] = make_pair(-min(ijk[edge[i].second.first], ijk[edge[i].second.second]), edge[i].second);
    }

    // エッジをソートし、最小全域木を構築
    sort(edge, edge + b);
    int s = 0;
    int w = 0;
    for (int i = 0; i < b; i++) {
        int l = edge[i].second.first;
        int r = edge[i].second.second;
        if (FIND(l) != FIND(r)) {
            if (ijk[l] > ijk[r]) swap(l, r);
            UNION(l, r);
            w++;
            if (w == a - 1) s = l;
        }
    }

    // DFSによる木の構築
    dfs(s);

    // クエリに対する応答
    for (int i = 0; i < d; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        x--; y--;  // 0-indexed に変換
        printf("%d\n", ijk[TO[query(0, SEG_SIZE / 2 - 1, min(F[x], F[y]), max(F[x], F[y]), 1)]]);
    }

    return 0;
}
accuracy: 1
time_ms: 120
memory_kb: 23652
final_rank: 1

code_snippet: /* -*- coding: utf-8 -*-
 *
 * 0575.cc:  JOI 国のお祭り事情(Festivals in JOI Kingdom) 
 */

#include<cstdio>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<utility>
#include<tuple>
 
using namespace std;

/* constant */

const int MAX_N = 100000;
const int MAX_M = 200000;
const int MAX_QN = 100000;
const int INF = 1 << 30;

/* typedef */

typedef set<int> si;
typedef pair<int,int> pii;
typedef vector<pii> vpii;
typedef tuple<int,int,int> tp3;

struct UFT {
  vector<int> links, ranks, sizes;
  UFT() {}

  void init(int n) {
    links.resize(n);
    for (int i = 0; i < n; i++) links[i] = i;
    ranks.assign(n, 1);
    sizes.assign(n, 1);
  }

  int root(int i) {
    int i0 = i;
    while (links[i0] != i0) i0 = links[i0];
    return (links[i] = i0);
  }

  int rank(int i) { return ranks[root(i)]; }
  int size(int i) { return sizes[root(i)]; }
  bool same(int i, int j) { return root(i) == root(j); }

  int merge(int i0, int i1) {
    int r0 = root(i0), r1 = root(i1), mr;
    if (r0 == r1) return r0;
    if (ranks[r0] == ranks[r1]) {
      links[r1] = r0;
      sizes[r0] += sizes[r1];
      ranks[r0]++;
      mr = r0;
    }
    else if (ranks[r0] > ranks[r1]) {
      links[r1] = r0;
      sizes[r0] += sizes[r1];
      mr = r0;
    }
    else {
      links[r0] = r1;
      sizes[r1] += sizes[r0];
      mr = r1;
    }
    return mr;
  }
};

/* global variables */

tp3 es[MAX_M];
vpii nbrs[MAX_N];
int fs[MAX_N], ds[MAX_N], ans[MAX_QN];
UFT uft;
si ss[MAX_N];
pii qs[MAX_QN];

/* subroutines */

/* main */

int main() {
  int n, m, k, qn;
  scanf("%d%d%d%d", &n, &m, &k, &qn);

  for (int i = 0; i < m; i++) {
    int a, b, l;
    scanf("%d%d%d", &a, &b, &l);
    a--, b--;
    es[i] = {0, a, b};
    nbrs[a].push_back(pii(b, l));
    nbrs[b].push_back(pii(a, l));
  }
  for (int i = 0; i < k; i++) scanf("%d", fs + i), fs[i]--;

  fill(ds, ds + n, INF);
  priority_queue<pii> q;
  for (int i = 0; i < k; i++) ds[fs[i]] = 0, q.push(pii(0, fs[i]));

  while (! q.empty()) {
    auto ue = q.top(); q.pop();
    int ud = -ue.first, u = ue.second;
    if (ds[u] != ud) continue;

    for (auto &vw: nbrs[u]) {
      int v = vw.first, vd = ud + vw.second;
      if (ds[v] > vd) {
	ds[v] = vd;
	q.push(pii(-vd, v));
      }
    }
  }
  //for (int u = 0; u < n; u++) printf("%d ", ds[u]); putchar('\n');

  for (int i = 0; i < m; i++) {
    auto [l, a, b] = es[i];
    es[i] = { min(ds[a], ds[b]) , a, b };
  }
  sort(es, es + m);

  uft.init(n);

  for (int i = 0; i < qn; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    u--, v--;
    ss[u].insert(i);
    ss[v].insert(i);
  }

  for (int i = m - 1; i >= 0; i--) {
    auto [l, u, v] = es[i];
    int ru = uft.root(u), rv = uft.root(v);
    if (ru != rv) {
      int ra = uft.merge(ru, rv), rb = (ra == ru) ? rv : ru;
      if (ss[ra].size() < ss[rb].size()) swap(ss[ra], ss[rb]);
      for (auto k: ss[rb]) {
	auto sit = ss[ra].find(k);
	if (sit != ss[ra].end()) {
	  ss[ra].erase(k);
	  ans[k] = l;
	}
	else {
	  ss[ra].insert(k);
	}
      }
    }
  }

  for (int i = 0; i < qn; i++) printf("%d\n", ans[i]);

  return 0;
}
accuracy: 1
time_ms: 220
memory_kb: 54576
final_rank: 10

code_snippet: #line 1 "a.cpp"
#define PROBLEM ""
#line 2 "/home/kuhaku/atcoder/github/algo/lib/graph/graph.hpp"
#include <iostream>
#include <vector>

/**
 * @brief 重み付きグラフ
 *
 * @tparam T 辺の重みの型
 */
template <class T>
struct Graph {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to(), _weight() {}
        constexpr _edge(int from, int to, T weight) : _from(from), _to(to), _weight(weight) {}
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }

        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr T weight() const { return _weight; }

      private:
        int _from, _to;
        T _weight;
    };

  public:
    using edge_type = typename Graph<T>::_edge;

    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}

    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }

    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to, T weight = T(1)) { edges[from].emplace_back(from, to, weight); }
    void add_edges(int from, int to, T weight = T(1)) {
        edges[from].emplace_back(from, to, weight);
        edges[to].emplace_back(to, from, weight);
    }

    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edge(from - base, to - base, weight);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edges(from - base, to - base, weight);
        }
    }

  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};

template <>
struct Graph<void> {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to() {}
        constexpr _edge(int from, int to) : _from(from), _to(to) {}

        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr int weight() const { return 1; }
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }

      private:
        int _from, _to;
    };

  public:
    using edge_type = typename Graph<void>::_edge;

    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}

    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }

    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to) { edges[from].emplace_back(from, to); }
    void add_edges(int from, int to) {
        edges[from].emplace_back(from, to);
        edges[to].emplace_back(to, from);
    }

    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edge(from - base, to - base);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edges(from - base, to - base);
        }
    }

  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};
#line 2 "/home/kuhaku/atcoder/github/algo/lib/template/template.hpp"
#pragma GCC target("sse4.2,avx2,bmi2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
template <class T, class U>
constexpr bool chmax(T &a, const U &b) {
    return a < (T)b ? a = (T)b, true : false;
}
template <class T, class U>
constexpr bool chmin(T &a, const U &b) {
    return (T)b < a ? a = (T)b, true : false;
}
constexpr std::int64_t INF = 1000000000000000003;
constexpr int Inf = 1000000003;
constexpr double EPS = 1e-7;
constexpr double PI = M_PI;
#line 3 "/home/kuhaku/atcoder/github/algo/lib/template/macro.hpp"
#define FOR(i, m, n) for (int i = (m); i < int(n); ++i)
#define FORR(i, m, n) for (int i = (m)-1; i >= int(n); --i)
#define FORL(i, m, n) for (int64_t i = (m); i < int64_t(n); ++i)
#define rep(i, n) FOR (i, 0, n)
#define repn(i, n) FOR (i, 1, n + 1)
#define repr(i, n) FORR (i, n, 0)
#define repnr(i, n) FORR (i, n + 1, 1)
#define all(s) (s).begin(), (s).end()
#line 3 "/home/kuhaku/atcoder/github/algo/lib/template/sonic.hpp"
struct Sonic {
    Sonic() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(20);
    }

    constexpr void operator()() const {}
} sonic;
#line 5 "/home/kuhaku/atcoder/github/algo/lib/template/atcoder.hpp"
using namespace std;
using ll = std::int64_t;
using ld = long double;
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
    return is >> p.first >> p.second;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (T &i : v) is >> i;
    return is;
}
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
    return os << '(' << p.first << ',' << p.second << ')';
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    for (auto it = v.begin(); it != v.end(); ++it) {
        os << (it == v.begin() ? "" : " ") << *it;
    }
    return os;
}
template <class Head, class... Tail>
void co(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cout << head << '\n';
    else std::cout << head << ' ', co(std::forward<Tail>(tail)...);
}
template <class Head, class... Tail>
void ce(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cerr << head << '\n';
    else std::cerr << head << ' ', ce(std::forward<Tail>(tail)...);
}
template <typename T, typename... Args>
auto make_vector(T x, int arg, Args... args) {
    if constexpr (sizeof...(args) == 0) return std::vector<T>(arg, x);
    else return std::vector(arg, make_vector<T>(x, args...));
}
void Yes(bool is_correct = true) {
    std::cout << (is_correct ? "Yes" : "No") << '\n';
}
void No(bool is_not_correct = true) {
    Yes(!is_not_correct);
}
void YES(bool is_correct = true) {
    std::cout << (is_correct ? "YES" : "NO") << '\n';
}
void NO(bool is_not_correct = true) {
    YES(!is_not_correct);
}
void Takahashi(bool is_correct = true) {
    std::cout << (is_correct ? "Takahashi" : "Aoki") << '\n';
}
void Aoki(bool is_not_correct = true) {
    Takahashi(!is_not_correct);
}
#line 3 "/home/kuhaku/atcoder/github/algo/lib/tree/union_find.hpp"

/**
 * @brief 素集合データ構造
 * @details Implement (union by size) + (path compression)
 * @see https://github.com/atcoder/ac-library/blob/master/atcoder/dsu.hpp
 */
struct union_find {
    union_find() = default;
    explicit union_find(int _n) : data(_n, -1) {}

    int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
    int get_root(int x) { return root(x); }

    bool is_root(int x) const { return data[x] < 0; }

    bool same(int x, int y) { return root(x) == root(y); }
    bool is_same(int x, int y) { return same(x, y); }

    int size(int x) { return -(data[root(x)]); }
    int get_size(int x) { return size(x); }

    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return true;
    }

    template <class F>
    bool unite(int x, int y, F f) {
        x = root(x), y = root(y);
        if (x != y) {
            if (data[x] > data[y]) std::swap(x, y);
            data[x] += data[y];
            data[y] = x;
        }
        f(x, y);
        return x != y;
    }

  private:
    std::vector<int> data;
};
#line 5 "a.cpp"

int main(void) {
    int n, m, k, q;
    cin >> n >> m >> k >> q;
    Graph<ll> g(n);
    g.input_edges(m);
    vector<int> a(k);
    cin >> a;
    vector<pair<int, int>> qs(q);
    cin >> qs;
    for (auto &[x, y] : qs) --x, --y;

    vector<ll> dist(n, Inf);
    for (auto x : a) dist[x - 1] = 0;
    priority_queue<pair<ll, ll>, vector<pair<ll, ll>>, greater<>> pq;
    for (auto x : a) pq.emplace(0, x - 1);
    while (!pq.empty()) {
        auto [d, x] = pq.top();
        pq.pop();
        if (dist[x] != d)
            continue;
        for (auto e : g[x]) {
            if (chmin(dist[e.to()], d + e.weight())) {
                pq.emplace(dist[e.to()], e.to());
            }
        }
    }

    vector<int> ord(n);
    iota(all(ord), 0);
    sort(all(ord), [&](int x, int y) {
        return dist[x] > dist[y];
    });
    vector<bool> used(n);
    vector<int> ls(q), rs(q, n);
    auto check = [&]() {
        rep (i, q) {
            if (ls[i] + 1 != rs[i])
                return false;
        }
        return true;
    };
    while (!check()) {
        vector mid(n, vector<int>());
        rep (i, q) mid[(ls[i] + rs[i]) / 2].emplace_back(i);
        union_find uf(n);
        vector<bool> used(n);
        rep (i, n) {
            for (auto x : mid[i]) {
                auto [l, r] = qs[x];
                if (uf.same(l, r))
                    rs[x] = i;
                else
                    ls[x] = i;
            }
            int x = ord[i];
            used[x] = true;
            for (auto e : g[x]) {
                if (used[e.to()])
                    uf.unite(x, e.to());
            }
        }
    }

    rep (i, q) {
        co(dist[ord[ls[i]]]);
    }

    return 0;
}
accuracy: 1
time_ms: 270
memory_kb: 24640
final_rank: 4

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cassert>
#include <chrono>
#include <random>
#include <set>
#include <utility>
#include <array>
#include <bitset>
#include <unordered_set>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n,m,k,q; cin >> n >> m >> k >> q;
    vector<vector<pair<int,int>>> g(n);
    for (int i = 0; i < m; ++i) {
        int x,y,z; cin >> x >> y >> z;
        x -= 1; y -= 1;
        g[x].push_back({y, z});
        g[y].push_back({x, z});
    }
    vector<int> d(n, 1e9);
    {
        using P = pair<int,int>;
        priority_queue<P,vector<P>,greater<>> pq;
        for (int i = 0; i < k; ++i) {
            int x; cin >> x; x -= 1;
            d[x] = 0;
            pq.push({0, x});
        }
        while (pq.size()) {
            auto p = pq.top(); pq.pop();
            int s = p.second;
            if (d[s] != p.first) continue;
            for (auto to : g[s]) {
                int t = to.first;
                int cost = d[s] + to.second;
                if (d[t] > cost) {
                    d[t] = cost;
                    pq.push({cost, t});
                }
            }
        }
    }
    vector<int> s(q), t(q);
    for (int i = 0; i < q; ++i) {
        cin >> s[i] >> t[i];
        s[i] -= 1; t[i] -= 1;
    }
    vector<pair<int,int>> dd(n);
    for (int i = 0; i < n; ++i) {
        dd[i] = {d[i], i};
    }
    sort(dd.rbegin(), dd.rend());
    vector<int> l(q, 0), r(q, 1e9);
    while (true) {
        vector<pair<int,int>> v(q);
        bool ed = true;
        for (int i = 0; i < q; ++i) {
            if (r[i]-l[i] > 1) ed = false;
            v[i] = {(l[i]+r[i])/2, i};
        }
        if (ed) break;
        sort(v.rbegin(), v.rend());

        UnionFind uf(n);
        vector<bool> used(n);
        int j = 0;
        for (int i = 0; i < q; ++i) {
            while (j < n and dd[j].first >= v[i].first) {
                int u = dd[j].second;
                used[u] = true;
                for (auto to : g[u]) {
                    if (used[to.first]) {
                        uf.unite(u, to.first);
                    }
                }
                j += 1;
            }
            int idx = v[i].second;
            if (uf.same(s[idx], t[idx])) {
                l[idx] = v[i].first;
            }
            else {
                r[idx] = v[i].first;
            }
        }
    }

    for (int i = 0; i < q; ++i) {
        cout << l[i] << "\n";
    }
}
accuracy: 1
time_ms: 270
memory_kb: 15776
final_rank: 2

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cassert>
#include <chrono>
#include <random>
#include <set>
#include <utility>
#include <array>
#include <bitset>
#include <unordered_set>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n,m,k,q; cin >> n >> m >> k >> q;
    vector<vector<pair<int,int>>> g(n);
    for (int i = 0; i < m; ++i) {
        int x,y,z; cin >> x >> y >> z;
        x -= 1; y -= 1;
        g[x].push_back({y, z});
        g[y].push_back({x, z});
    }
    vector<int> d(n, 1e9);
    {
        using P = pair<int,int>;
        priority_queue<P,vector<P>,greater<>> pq;
        for (int i = 0; i < k; ++i) {
            int x; cin >> x; x -= 1;
            d[x] = 0;
            pq.push({0, x});
        }
        while (pq.size()) {
            auto p = pq.top(); pq.pop();
            int s = p.second;
            if (d[s] != p.first) continue;
            for (auto to : g[s]) {
                int t = to.first;
                int cost = d[s] + to.second;
                if (d[t] > cost) {
                    d[t] = cost;
                    pq.push({cost, t});
                }
            }
        }
    }
    vector<int> s(q), t(q);
    for (int i = 0; i < q; ++i) {
        cin >> s[i] >> t[i];
        s[i] -= 1; t[i] -= 1;
    }
    vector<pair<int,int>> dd(n);
    for (int i = 0; i < n; ++i) {
        dd[i] = {d[i], i};
    }
    sort(dd.rbegin(), dd.rend());
    vector<int> l(q, 0), r(q, 1e9);
    while (r[0]-l[0] > 1) {
        vector<pair<int,int>> v(q);
        for (int i = 0; i < q; ++i) {
            v[i] = {(l[i]+r[i])/2, i};
        }
        sort(v.rbegin(), v.rend());

        UnionFind uf(n);
        vector<bool> used(n);
        int j = 0;
        for (int i = 0; i < q; ++i) {
            while (j < n and dd[j].first >= v[i].first) {
                int u = dd[j].second;
                used[u] = true;
                for (auto to : g[u]) {
                    if (used[to.first]) {
                        uf.unite(u, to.first);
                    }
                }
                j += 1;
            }
            int idx = v[i].second;
            if (uf.same(s[idx], t[idx])) {
                l[idx] = v[i].first;
            }
            else {
                r[idx] = v[i].first;
            }
        }
    }

    for (int i = 0; i < q; ++i) {
        cout << l[i] << "\n";
    }
}
accuracy: 0.2903225806451613
time_ms: 100
memory_kb: 13404
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll=long long;
using pll=pair<ll,ll>;
using tll=tuple<ll,ll,ll>;
using ld=long double;
const ll INF=(1ll<<60);
#define rep(i,n) for (ll i=0;i<(ll)(n);i++)
#define all(v) v.begin(),v.end()
template<class T> void chmin(T &a,T b){
    if(a>b){
        a=b;
    }
}
template<class T> void chmax(T &a,T b){
    if(a<b){
        a=b;
    }
}
struct union_find{
    vector<ll> par,siz;
    union_find(ll n):par(n),siz(n,1){
        rep(i,n){
            par[i]=i;
        }
    }
    ll root(ll x){
        if(par[x]==x) return x;
        return par[x]=root(par[x]);
    }
    void unite(ll x,ll y){
        ll rx=root(x);
        ll ry=root(y);
        if(rx==ry) return;
        if(siz[rx]<siz[ry]) swap(rx,ry);
        par[ry]=rx;
        siz[rx]+=siz[ry];
    }
    bool same(ll x,ll y){
        ll rx=root(x);
        ll ry=root(y);
        return rx==ry;
    }
    ll size(ll x){
        return siz[root(x)];
    }
    vector<ll> allsize(){
        set<ll> st;
        vector<ll> ret;
        rep(i,siz.size()){
            ll rx=root(i);
            if(st.count(rx)) continue;
            ret.push_back(siz[rx]);
            st.insert(rx);
        }
        return ret;
    }
};
struct tree{
    ll n,max_k=1;
    vector<vector<pair<ll,ll>>> g;
    vector<vector<ll>> parent;
    vector<vector<ll>> mn;
    vector<ll> depth;
    tree(ll g_size){
        n=g_size;
        g.resize(n);
    }
    void add_edge(ll u,ll v,ll w){
        g[u].emplace_back(v,w);
        g[v].emplace_back(u,w);
    }
    void LCA(ll root=0){
        while((1<<max_k)<n) max_k++;
        parent.assign(max_k,vector<ll>(n,-1));
        mn.assign(max_k,vector<ll>(n,INF));
        depth.assign(n,-1);
        dfs(root,-1,0);
        for(ll k=0;k+1<max_k;k++){
            for(ll i=0;i<n;i++){
                if(parent[k][i]==-1){
                    parent[k+1][i]=-1;
                }else{
                    parent[k+1][i]=parent[k][parent[k][i]];
                    chmin(mn[k+1][i],min(mn[k][i],mn[k][parent[k][i]]));
                }
            }
        }
    }
    void dfs(ll v,ll par,ll d){
        parent[0][v]=par;
        depth[v]=d;
        for(auto &[i,w]:g[v]){
            if(i==par){
                mn[0][v]=w;
            }else{
                dfs(i,v,d+1);
            }
        }
    }
    ll get_lca(ll u,ll v){
        if(depth[u]<depth[v]) swap(u,v);
        ll diff=depth[u]-depth[v];
        for(ll k=0;k<max_k;k++){
            if(diff&(1<<k)){
                u=parent[k][u];
            }
        }
        if(u==v) return u;
        for(ll k=max_k-1;0<=k;k--){
            if(parent[k][u]!=parent[k][v]){
                u=parent[k][u];
                v=parent[k][v];
            }
        }
        return parent[0][u];
    }
    ll f(ll x,ll l){
        ll diff=depth[x]-depth[l];
        ll ret=INF;
        rep(i,max_k){
            if(diff&(1<<i)){
                chmin(ret,mn[i][x]);
                x=parent[i][x];
            }
        }
        return ret;
    }
    ll q(ll u,ll v){
        ll l=get_lca(u,v);
        return min(f(u,l),f(v,l));
    }
};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n,m,k,q;
    cin >> n >> m >> k >> q;
    vector<vector<pll>> g(n);
    while(m--){
        ll a,b,l;
        cin >> a >> b >> l;
        a--;
        b--;
        g[a].push_back({b,l});
        g[b].push_back({a,l});
    }
    vector<ll> f(k);
    rep(i,k){
        cin >> f[i];
        f[i]--;
    }
    vector<ll> dist(n,INF);
    priority_queue<pll,vector<pll>,greater<pll>> pq;
    rep(i,k){
        pq.push({0,f[i]});
        dist[f[i]]=0;
    }
    while(!pq.empty()){
        ll d,x;
        tie(d,x)=pq.top();
        pq.pop();
        for(auto &[i,w]:g[x]){
            if(dist[x]+w<dist[i]){
                dist[i]=dist[x]+w;
                pq.push({dist[i],i});
            }
        }
    }
    priority_queue<tll> pq2;
    rep(i,g.size()){
        for(auto &[j,w]:g[i]){
            pq2.push({min(dist[i],dist[j]),i,j});
        }
    }
    vector<tll> e;
    union_find uf(n);
    while(!pq2.empty()){
        ll w,i,j;
        tie(w,i,j)=pq2.top();
        pq2.pop();
        if(!uf.same(i,j)){
            uf.unite(i,j);
            e.emplace_back(i,j,w);
        }
    }
    tree tr(n);
    for(auto &[i,j,w]:e){
        tr.add_edge(i,j,w);
    }
    tr.LCA(0);
    vector<ll> now_min(n,0);
    while(q--){
        ll s,t;
        cin >> s >> t;
        s--;
        t--;
        cout << tr.q(s,t) << '\n';
    }
}
accuracy: 1
time_ms: 210
memory_kb: 67396
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll=int;
using pll=pair<ll,ll>;
using tll=tuple<ll,ll,ll>;
using ld=long double;
const ll INF=(1ll<<30);
#define rep(i,n) for (ll i=0;i<(ll)(n);i++)
#define all(v) v.begin(),v.end()
template<class T> void chmin(T &a,T b){
    if(a>b){
        a=b;
    }
}
template<class T> void chmax(T &a,T b){
    if(a<b){
        a=b;
    }
}
struct union_find{
    vector<ll> par,siz;
    union_find(ll n):par(n),siz(n,1){
        rep(i,n){
            par[i]=i;
        }
    }
    ll root(ll x){
        if(par[x]==x) return x;
        return par[x]=root(par[x]);
    }
    void unite(ll x,ll y){
        ll rx=root(x);
        ll ry=root(y);
        if(rx==ry) return;
        if(siz[rx]<siz[ry]) swap(rx,ry);
        par[ry]=rx;
        siz[rx]+=siz[ry];
    }
    bool same(ll x,ll y){
        ll rx=root(x);
        ll ry=root(y);
        return rx==ry;
    }
    ll size(ll x){
        return siz[root(x)];
    }
    vector<ll> allsize(){
        set<ll> st;
        vector<ll> ret;
        rep(i,siz.size()){
            ll rx=root(i);
            if(st.count(rx)) continue;
            ret.push_back(siz[rx]);
            st.insert(rx);
        }
        return ret;
    }
};
struct tree{
    int n,max_k=1;
    vector<vector<pair<int,int>>> g;
    vector<vector<int>> parent;
    vector<vector<int>> mn;
    vector<int> depth;
    tree(int g_size){
        n=g_size;
        g.resize(n);
    }
    void add_edge(int u,int v,int w){
        g[u].emplace_back(v,w);
        g[v].emplace_back(u,w);
    }
    void LCA(int root=0){
        while((1<<max_k)<n) max_k++;
        parent.assign(max_k,vector<int>(n,-1));
        mn.assign(max_k,vector<int>(n,INF));
        depth.assign(n,-1);
        dfs(root,-1,0);
        for(int k=0;k+1<max_k;k++){
            for(int i=0;i<n;i++){
                if(parent[k][i]==-1){
                    parent[k+1][i]=-1;
                }else{
                    parent[k+1][i]=parent[k][parent[k][i]];
                    chmin(mn[k+1][i],min(mn[k][i],mn[k][parent[k][i]]));
                }
            }
        }
    }
    void dfs(int v,int par,int d){
        parent[0][v]=par;
        depth[v]=d;
        for(auto &[i,w]:g[v]){
            if(i==par){
                mn[0][v]=w;
            }else{
                dfs(i,v,d+1);
            }
        }
    }
    int get_lca(int u,int v){
        if(depth[u]<depth[v]) swap(u,v);
        int diff=depth[u]-depth[v];
        for(int k=0;k<max_k;k++){
            if(diff&(1<<k)){
                u=parent[k][u];
            }
        }
        if(u==v) return u;
        for(int k=max_k-1;0<=k;k--){
            if(parent[k][u]!=parent[k][v]){
                u=parent[k][u];
                v=parent[k][v];
            }
        }
        return parent[0][u];
    }
    int f(int x,int l){
        int diff=depth[x]-depth[l];
        int ret=INF;
        rep(i,max_k){
            if(diff&(1<<i)){
                chmin(ret,mn[i][x]);
                x=parent[i][x];
            }
        }
        return ret;
    }
    int q(int u,int v){
        int l=get_lca(u,v);
        return min(f(u,l),f(v,l));
    }
};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n,m,k,q;
    cin >> n >> m >> k >> q;
    vector<vector<pll>> g(n);
    while(m--){
        ll a,b,l;
        cin >> a >> b >> l;
        a--;
        b--;
        g[a].push_back({b,l});
        g[b].push_back({a,l});
    }
    vector<ll> f(k);
    rep(i,k){
        cin >> f[i];
        f[i]--;
    }
    vector<ll> dist(n,INF);
    priority_queue<pll,vector<pll>,greater<pll>> pq;
    rep(i,k){
        pq.push({0,f[i]});
        dist[f[i]]=0;
    }
    while(!pq.empty()){
        ll d,x;
        tie(d,x)=pq.top();
        pq.pop();
        for(auto &[i,w]:g[x]){
            if(dist[x]+w<dist[i]){
                dist[i]=dist[x]+w;
                pq.push({dist[i],i});
            }
        }
    }
    priority_queue<tll> pq2;
    rep(i,g.size()){
        for(auto &[j,w]:g[i]){
            pq2.push({min(dist[i],dist[j]),i,j});
        }
    }
    vector<tll> e;
    union_find uf(n);
    while(!pq2.empty()){
        ll w,i,j;
        tie(w,i,j)=pq2.top();
        pq2.pop();
        if(!uf.same(i,j)){
            uf.unite(i,j);
            e.emplace_back(i,j,w);
        }
    }
    tree tr(n);
    for(auto &[i,j,w]:e){
        tr.add_edge(i,j,w);
    }
    tr.LCA(0);
    vector<ll> now_min(n,0);
    while(q--){
        ll s,t;
        cin >> s >> t;
        s--;
        t--;
        cout << tr.q(s,t) << '\n';
    }
}
accuracy: 1
time_ms: 170
memory_kb: 41348
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll=int;
using pll=pair<ll,ll>;
using tll=tuple<ll,ll,ll>;
using ld=long double;
const ll INF=(1ll<<30);
#define rep(i,n) for (ll i=0;i<(ll)(n);i++)
#define all(v) v.begin(),v.end()
template<class T> void chmin(T &a,T b){
    if(a>b){
        a=b;
    }
}
template<class T> void chmax(T &a,T b){
    if(a<b){
        a=b;
    }
}
struct union_find{
    vector<ll> par,siz;
    union_find(ll n):par(n),siz(n,1){
        rep(i,n){
            par[i]=i;
        }
    }
    ll root(ll x){
        if(par[x]==x) return x;
        return par[x]=root(par[x]);
    }
    void unite(ll x,ll y){
        ll rx=root(x);
        ll ry=root(y);
        if(rx==ry) return;
        if(siz[rx]<siz[ry]) swap(rx,ry);
        par[ry]=rx;
        siz[rx]+=siz[ry];
    }
    bool same(ll x,ll y){
        ll rx=root(x);
        ll ry=root(y);
        return rx==ry;
    }
    ll size(ll x){
        return siz[root(x)];
    }
    vector<ll> allsize(){
        set<ll> st;
        vector<ll> ret;
        rep(i,siz.size()){
            ll rx=root(i);
            if(st.count(rx)) continue;
            ret.push_back(siz[rx]);
            st.insert(rx);
        }
        return ret;
    }
};
struct HLD{
    int n;
    vector<vector<int>> g;
    vector<int> par,sz,in,out,head;
    int ord=0;
    HLD(int x){
        n=x;
        g.resize(n);
        par.resize(n);
        sz.assign(n,0);
        in.resize(n);
        out.resize(n);
        head.resize(n);
    }
    void add_edge(int u,int v){
        g[u].push_back(v);
        g[v].push_back(u);
    }
    void dfs_sz(int x,int p){
        sz[x]++;
        if(0<g[x].size()&&g[x].front()==p) swap(g[x].front(),g[x].back());
        for(auto &i:g[x]){
            if(i==p) continue;
            dfs_sz(i,x);
            sz[x]+=sz[i];
            if(sz[g[x].front()]<sz[i]) swap(g[x].front(),i);
        }
    }
    void dfs_hld(int x,int p){
        par[x]=p;
        in[x]=ord;
        ord++;
        for(auto &i:g[x]){
            if(i==p) continue;
            if(i==g[x].front()) head[i]=head[x];
            else head[i]=i;
            dfs_hld(i,x);
        }
        out[x]=ord;
    }
    void solve(int root){
        dfs_sz(root,-1);
        dfs_hld(root,-1);
    }
    vector<pair<int,int>> query(int u,int v,bool edge){
        vector<pair<int,int>> ret;
        while(true){
            if(in[v]<in[u]) swap(u,v);
            if(head[u]==head[v]) break;
            ret.emplace_back(in[head[v]],in[v]+1);
            v=par[head[v]];
        }
        ret.emplace_back(in[u]+edge,in[v]+1);
        return ret;
    }
};
struct segment_tree{
    vector<ll> v;
    ll n;
    segment_tree(ll x){
        ll s=1;
        while(s<x) s*=2;
        n=s;
        v.assign(s*2-1,INF);
    }
    ll get(ll i){
        i+=n-1;
        return v[i];
    }
    void update(ll i,ll x){
        i+=n-1;
        v[i]=x;
        while(0<i){
            i=(i-1)/2;
            v[i]=min(v[i*2+1],v[i*2+2]);
        }
    }
    ll query(ll a,ll b){
        return query_sub(a,b,0ll,0ll,n);
    }
    ll query_sub(ll a,ll b,ll k,ll l,ll r){
        if(r<=a||b<=l){
            return INF;
        }else if(a<=l&&r<=b){
            return v[k];
        }else{
            ll vl=query_sub(a,b,k*2+1,l,(l+r)/2);
            ll vr=query_sub(a,b,k*2+2,(l+r)/2,r);
            return min(vl,vr);
        }
    }
};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n,m,k,q;
    cin >> n >> m >> k >> q;
    vector<vector<pll>> g(n);
    while(m--){
        ll a,b,l;
        cin >> a >> b >> l;
        a--;
        b--;
        g[a].push_back({b,l});
        g[b].push_back({a,l});
    }
    vector<ll> f(k);
    rep(i,k){
        cin >> f[i];
        f[i]--;
    }
    vector<ll> dist(n,INF);
    priority_queue<pll,vector<pll>,greater<pll>> pq;
    rep(i,k){
        pq.push({0,f[i]});
        dist[f[i]]=0;
    }
    while(!pq.empty()){
        ll d,x;
        tie(d,x)=pq.top();
        pq.pop();
        for(auto &[i,w]:g[x]){
            if(dist[x]+w<dist[i]){
                dist[i]=dist[x]+w;
                pq.push({dist[i],i});
            }
        }
    }
    priority_queue<tll> pq2;
    rep(i,g.size()){
        for(auto &[j,w]:g[i]){
            pq2.push({min(dist[i],dist[j]),i,j});
        }
    }
    vector<tll> e;
    union_find uf(n);
    while(!pq2.empty()){
        ll w,i,j;
        tie(w,i,j)=pq2.top();
        pq2.pop();
        if(!uf.same(i,j)){
            uf.unite(i,j);
            e.emplace_back(i,j,w);
        }
    }
    HLD hld(n);
    for(auto &[i,j,w]:e){
        hld.add_edge(i,j);
    }
    hld.solve(0);
    segment_tree st(n);
    for(auto &[i,j,w]:e){
        vector<pll> v=hld.query(i,j,true);
        for(auto &[l,r]:v){
            if(l==r) continue;
            st.update(l,w);
        }
    }
    vector<ll> now_min(n,0);
    while(q--){
        ll s,t;
        cin >> s >> t;
        s--;
        t--;
        vector<pll> v=hld.query(s,t,true);
        ll ans=INF;
        for(auto &[l,r]:v){
            chmin(ans,st.query(l,r));
        }
        cout << ans << '\n';
    }
}
accuracy: 1
time_ms: 250
memory_kb: 30080
final_rank: 5

code_snippet: #include <bits/stdc++.h>
#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#define rep(i, x) for (int i = 0; i < x; i++)
#define all(a) (a).begin(), (a).end()
using namespace std;
using ll = long long;
constexpr ll INF = 20000000001;

typedef pair<ll, int> P;

struct edge
{
    int to;
    ll cost;
};

struct UnionFind
{
    int n;
    vector<int> par;
    vector<int> depth;
    UnionFind(int kn)
    {
        n = kn;
        par.resize(n);
        rep(i, n) { par[i] = i; }
        depth.resize(n, 1);
    }
    int find(int x)
    {
        if (x == par[x])
        {
            return x;
        }
        return par[x] = find(par[x]);
    }
    void unite(int a, int b)
    {
        a = find(a);
        b = find(b);
        if (a == b)
        {
            return;
        }
        if (depth[a] < depth[b])
        {
            par[a] = b;
        }
        else
        {
            par[b] = a;
            if (depth[a] == depth[b])
            {
                depth[a]++;
            }
        }
    }
    bool same(int a, int b)
    {
        return find(a) == find(b);
    }
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m, k, q;
    cin >> n >> m >> k >> q;
    vector<vector<edge>> G(n);
    rep(i, m)
    {
        int a, b;
        ll c;
        cin >> a >> b >> c;
        a--;
        b--;
        G[a].push_back({b, c});
        G[b].push_back({a, c});
    }
    // vector<int> fes;
    priority_queue<P, vector<P>, greater<P>> que; // P(距離、頂点)
    rep(i, k)
    {
        int f;
        cin >> f;
        f--;
        // fes.push_back(f);
        que.push(P(0, f));
    }
    vector<bool> used(n, false);
    vector<P> V; // P(距離、頂点)の大きい順に入っている
    while (!que.empty())
    {
        P p = que.top();
        que.pop();
        ll c = p.first;
        int v = p.second;
        if (used[v])
        {
            continue;
        }
        V.push_back(p);
        used[v] = true;
        for (auto e : G[v])
        {
            if (used[e.to])
            {
                continue;
            }
            que.push(P(c + e.cost, e.to));
        }
    }
    reverse(all(V));

    vector<int> s(q), t(q);
    vector<ll> l(q, 0), r(q, INF);
    vector<P> mid_idx;
    rep(i, q)
    {
        cin >> s[i] >> t[i];
        s[i]--;
        t[i]--;
        mid_idx.push_back(P((l[i] + r[i]) / 2, i));
    }
    rep(z, 40)
    {
        if (mid_idx.size() == 0)
        {
            break;
        }
        sort(all(mid_idx));
        reverse(all(mid_idx));

        UnionFind uf(n);
        vector<bool> visited(n);
        vector<P> new_mid_idx;
        int V_idx = 0;
        for (auto p : mid_idx)
        {
            while (V_idx != V.size() && V[V_idx].first >= p.first)
            {
                visited[V[V_idx].second] = true;
                for (auto e : G[V[V_idx].second])
                {
                    if (visited[e.to])
                    {
                        uf.unite(V[V_idx].second, e.to);
                    }
                }
                V_idx++;
            }
            if (uf.same(s[p.second], t[p.second]))
            {
                l[p.second] = p.first;
            }
            else
            {
                r[p.second] = p.first;
            }
            if (r[p.second] - l[p.second] > 1)
            {
                new_mid_idx.push_back(P((l[p.second] + r[p.second]) / 2, p.second));
            }
        }
        mid_idx = new_mid_idx;
    }
    rep(i, q)
    {
        cout << l[i] << endl;
    }
}
accuracy: 1
time_ms: 420
memory_kb: 27012
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using tll = tuple<ll, ll, ll>;

constexpr ll INF = 1LL << 60;
ll N, M, K, Q;
vector<vector<pll>> G;
vector<vector<ll>> T;
vector<ll> F, D;
vector<tll> E;

class DisjointSet {
public:
  ll n;
  vector<ll> node, rank;
  DisjointSet(ll n_) : n(n_) {
    node.resize(n);
    rank.resize(n, 1);
    for (ll i = 0; i < n; i++) node[i] = i;
  }
  ll find(ll x) {
    if (node[x] != x) {
      node[x] = find(node[x]);
    }
    return node[x];
  }
  bool same(ll x, ll y) {
    return find(x) == find(y);
  }
  void unite(ll x, ll y) {
    ll px = find(x);
    ll py = find(y);
    if (rank[px] < rank[py]) {
      node[px] = py;
      rank[py] += rank[px];
    } else {
      node[py] = px;
      rank[px] += rank[py];
    }
  }
};

class LCA {
public:
  ll n, k;
  vector<vector<ll>> tree;
  vector<vector<pll>> parent;
  vector<ll> dist, cost;

  LCA(vector<vector<ll>> tree_, vector<ll> cost_) : n((ll)tree_.size()), dist(n, -1) {
    tree = tree_;
    cost = cost_;
    k = 1;
    while (n > (1LL << k)) k++;
    parent.assign(n, vector<pll>(k + 1, pll(-1, INF)));
    dfs(0, -1, 0);
    doubling();
  }
  void dfs(ll u, ll p, ll cnt) {
    dist[u] = cnt;
    parent[u][0] = {p, p != -1 ? min(cost[u], cost[p]) : cost[u]};
    for (auto v : tree[u]) {
      if (dist[v] >= 0) continue;
      dfs(v, u, cnt + 1);
    }
  }
  void doubling() {
    for (ll i = 1; i <= k; i++) {
      for (ll j = 0; j < n; j++) {
        auto [a1, c1] = parent[j][i - 1];
        if (a1 == -1) {
          parent[j][i] = {a1, c1};
        } else {
          auto [a2, c2] = parent[a1][i - 1];
          parent[j][i] = {a2, min(c1, c2)};
        }
      }
    }
  }
  ll get_cost(ll a, ll b) {
    if (dist[a] < dist[b]) swap(a, b);
    ll ret = INF;
    for (ll i = 0; i <= k; i++) {
      if ((dist[a] - dist[b]) >> i & 1) {
        ret = min(ret, parent[a][i].second);
        a = parent[a][i].first;
      }
    }
    if (a == b) return ret;
    for (ll i = k; i >= 0; i--) {
      if (parent[a][i].first == parent[b][i].first) continue;
      ret = min({ret, parent[a][i].second, parent[b][i].second});
      a = parent[a][i].first;
      b = parent[b][i].first;
    }
    return min({ret, parent[a][0].second, parent[b][0].second});
  }
};

void dijkstra() {
  D.resize(N, INF);
  priority_queue<pll, vector<pll>, greater<pll>> que;
  for (auto f : F) {
    que.push({0, f});
    D[f] = 0;
  }
  while (!que.empty()) {
    auto [ud, u] = que.top(); que.pop();
    if (D[u] < ud) continue;
    for (auto [v, vd] : G[u]) {
      if (D[v] <= ud + vd) continue;
      D[v] = ud + vd;
      que.push({D[v], v});
    }
  }
}

void prim() {
  T.resize(N);
  for (auto& [c, a, b] : E) c = min(D[a], D[b]);
  sort(E.begin(), E.end(), greater<tll>());
  DisjointSet ds(N);
  for (auto [c, a, b] : E) {
    if (ds.same(a, b)) continue;
    T[a].push_back(b);
    T[b].push_back(a);
    ds.unite(a, b);
  }
}

int main() {
  cin >> N >> M >> K >> Q;
  G.resize(N);
  F.resize(K);
  for (ll i = 0; i < M; i++) {
    ll a, b, l;
    cin >> a >> b >> l;
    a--; b--;
    G[a].push_back({b, l});
    G[b].push_back({a, l});
    E.push_back({0, a, b});
  }
  for (ll i = 0; i < K; i++) {
    cin >> F[i];
    F[i]--;
  }
  dijkstra();
  prim();
  LCA lca(T, D);
  vector<ll> ans;
  while (Q--) {
    ll s, t;
    cin >> s >> t;
    s--; t--;
    ans.push_back(lca.get_cost(s, t));
  }
  for (auto a : ans) cout << a << endl;
  return 0;
}
accuracy: 1
time_ms: 330
memory_kb: 73588
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using tll = tuple<ll, ll, ll>;

constexpr ll INF = 1LL << 60;
ll N, M, K, Q;
vector<vector<pll>> G;
vector<vector<ll>> T;
vector<ll> F, D;
vector<tll> E;

class DisjointSet {
public:
  ll n;
  vector<ll> node, rank;
  DisjointSet(ll n_) : n(n_) {
    node.resize(n);
    rank.resize(n, 1);
    for (ll i = 0; i < n; i++) node[i] = i;
  }
  ll find(ll x) {
    if (node[x] != x) {
      node[x] = find(node[x]);
    }
    return node[x];
  }
  bool same(ll x, ll y) {
    return find(x) == find(y);
  }
  void unite(ll x, ll y) {
    ll px = find(x);
    ll py = find(y);
    if (rank[px] < rank[py]) {
      node[px] = py;
      rank[py] += rank[px];
    } else {
      node[py] = px;
      rank[px] += rank[py];
    }
  }
};

class LCA {
public:
  ll n, k;
  vector<vector<ll>> tree;
  vector<vector<pll>> parent;
  vector<ll> dist, cost;

  LCA(vector<vector<ll>> tree_, vector<ll> cost_) : n((ll)tree_.size()), dist(n, -1) {
    tree = tree_;
    cost = cost_;
    k = 1;
    while (n > (1LL << k)) k++;
    parent.assign(n, vector<pll>(k + 1, pll(-1, INF)));
    dfs(0, -1, 0);
    doubling();
  }
  void dfs(ll u, ll p, ll cnt) {
    dist[u] = cnt;
    parent[u][0] = {p, p != -1 ? min(cost[u], cost[p]) : cost[u]};
    for (auto v : tree[u]) {
      if (dist[v] >= 0) continue;
      dfs(v, u, cnt + 1);
    }
  }
  void doubling() {
    for (ll i = 1; i <= k; i++) {
      for (ll j = 0; j < n; j++) {
        auto [a1, c1] = parent[j][i - 1];
        if (a1 == -1) {
          parent[j][i] = {a1, c1};
        } else {
          auto [a2, c2] = parent[a1][i - 1];
          parent[j][i] = {a2, min(c1, c2)};
        }
      }
    }
  }
  ll get_cost(ll a, ll b) {
    if (dist[a] < dist[b]) swap(a, b);
    ll ret = INF;
    for (ll i = 0; i <= k; i++) {
      if ((dist[a] - dist[b]) >> i & 1) {
        ret = min(ret, parent[a][i].second);
        a = parent[a][i].first;
      }
    }
    if (a == b) return ret;
    for (ll i = k; i >= 0; i--) {
      if (parent[a][k].first == parent[b][k].first) continue;
      ret = min({ret, parent[a][k].second, parent[b][k].second});
      a = parent[a][k].first;
      b = parent[b][k].first;
    }
    return min({ret, parent[a][0].second, parent[b][0].second});
  }
};

void dijkstra() {
  D.resize(N, INF);
  priority_queue<pll, vector<pll>, greater<pll>> que;
  for (auto f : F) {
    que.push({0, f});
    D[f] = 0;
  }
  while (!que.empty()) {
    auto [ud, u] = que.top(); que.pop();
    if (D[u] < ud) continue;
    for (auto [v, vd] : G[u]) {
      if (D[v] <= ud + vd) continue;
      D[v] = ud + vd;
      que.push({D[v], v});
    }
  }
}

void prim() {
  T.resize(N);
  for (auto& [c, a, b] : E) c = min(D[a], D[b]);
  sort(E.begin(), E.end(), greater<tll>());
  DisjointSet ds(N);
  for (auto [c, a, b] : E) {
    if (ds.same(a, b)) continue;
    T[a].push_back(b);
    T[b].push_back(a);
    ds.unite(a, b);
  }
}

int main() {
  cin >> N >> M >> K >> Q;
  G.resize(N);
  F.resize(K);
  for (ll i = 0; i < M; i++) {
    ll a, b, l;
    cin >> a >> b >> l;
    a--; b--;
    G[a].push_back({b, l});
    G[b].push_back({a, l});
    E.push_back({0, a, b});
  }
  for (ll i = 0; i < K; i++) {
    cin >> F[i];
    F[i]--;
  }
  dijkstra();
  prim();
  LCA lca(T, D);
  vector<ll> ans;
  while (Q--) {
    ll s, t;
    cin >> s >> t;
    s--; t--;
    ans.push_back(lca.get_cost(s, t));
  }
  for (auto a : ans) cout << a << endl;
  return 0;
}
accuracy: 0.12903225806451613
time_ms: 200
memory_kb: 72808
final_rank: 20