#include <bits/stdc++.h>
using namespace std;
struct UnionFind {
  int lenght;
  vector<int> parent;
  vector<int> depth;
  UnionFind(int _size) {
    lenght = _size;
    for (int i = 0; i < lenght; i++) {
      parent.push_back(i);
      depth.push_back(1);
    }
  }
  int find(int first) {
    return (first == parent[first] ? first
                                   : parent[first] = find(parent[first]));
  }
  int merge(int first, int second) {
    first = find(first);
    second = find(second);
    if (first == second) return 0;
    if (depth[first] < depth[second]) swap(first, second);
    depth[first] += depth[second];
    parent[second] = first;
    return 1;
  }
};
const long long INF = 1e18 + 10;
const int MAXN = 1e5 + 10, LOG = 25;
int n, m, k, q;
long long dist[MAXN];
vector<pair<int, long long> > graph[MAXN];
vector<pair<int, long long> > tree[MAXN];
int anc[MAXN][LOG];
long long maxi[MAXN][LOG];
int depth[MAXN];
void dfs(int par, int node, int d, int w) {
  anc[node][0] = par;
  maxi[node][0] = w;
  depth[node] = d;
  for (int i = 1; i < LOG; i++) {
    anc[node][i] = anc[anc[node][i - 1]][i - 1];
    maxi[node][i] = max(maxi[node][i - 1], maxi[anc[node][i - 1]][i - 1]);
  }
  for (auto p : tree[node]) {
    int where = p.first;
    long long wi = p.second;
    if (where != par) dfs(node, where, d + 1, wi);
  }
}
int lca(int first, int second) {
  if (depth[first] > depth[second]) swap(first, second);
  for (int i = LOG - 1; i >= 0; i--) {
    int ny = anc[second][i];
    if (depth[ny] >= depth[first]) second = ny;
  }
  if (first == second) return first;
  for (int i = LOG - 1; i >= 0; i--) {
    int nx = anc[first][i];
    int ny = anc[second][i];
    if (nx != ny) {
      first = nx;
      second = ny;
    }
  }
  return anc[first][0];
}
long long res(int first, int second) {
  if (first == second) return 0;
  if (depth[first] > depth[second]) swap(first, second);
  int z = lca(first, second);
  int sub = 0;
  if (first == z) sub = 1;
  long long rez = 0;
  for (int i = LOG - 1; i >= 0; i--) {
    int ny = anc[second][i];
    if (depth[ny] >= depth[first] + sub) {
      rez = max(rez, maxi[second][i]);
      second = ny;
    }
  }
  rez = max(rez, maxi[second][0]);
  if (first == z) return rez;
  for (int i = LOG - 1; i >= 0; i--) {
    int nx = anc[first][i];
    int ny = anc[second][i];
    if (nx != ny) {
      first = nx;
      second = ny;
      rez = max(rez, maxi[first][i]);
      rez = max(rez, maxi[second][i]);
    }
  }
  rez = max(rez, maxi[first][0]);
  rez = max(rez, maxi[second][0]);
  return rez;
}
int main() {
  cin >> n >> m >> k >> q;
  UnionFind UF(n);
  for (int i = 0; i < m; i++) {
    int first, second;
    long long z;
    cin >> first >> second >> z;
    first--;
    second--;
    graph[first].push_back({second, z});
    graph[second].push_back({first, z});
  }
  vector<pair<int, int> > qs;
  for (int i = 0; i < q; i++) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    qs.push_back({a, b});
  }
  set<pair<long long, int> > s;
  for (int i = 0; i < n; i++) {
    pair<long long, int> p = {INF, i};
    if (i < k) p.first = 0;
    s.insert(p);
    dist[i] = p.first;
  }
  int br = 0;
  for (int i = 0; i < n; i++) {
    pair<long long, int> p = *s.begin();
    s.erase(s.find(p));
    if (n == 100) {
      br++;
    }
    int node = p.second;
    for (auto pi : graph[node]) {
      int where = pi.first;
      long long w = pi.second;
      long long di = dist[node] + w;
      if (dist[where] <= di) continue;
      s.erase(s.find({dist[where], where}));
      dist[where] = di;
      s.insert({dist[where], where});
    }
  }
  vector<pair<long long, pair<int, int> > > v;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < (int)graph[i].size(); j++) {
      int first = graph[i][j].first;
      if (first > i) continue;
      graph[i][j].second += dist[i] + dist[first];
      v.push_back({graph[i][j].second, {i, first}});
    }
  }
  sort((v).begin(), (v).end());
  vector<pair<pair<int, int>, long long> > include;
  for (int i = 0; i < m; i++) {
    int a = v[i].second.first;
    int b = v[i].second.second;
    if (UF.merge(a, b)) include.push_back({v[i].second, v[i].first});
  }
  for (int i = 0; i < n - 1; i++) {
    int a = include[i].first.first;
    int b = include[i].first.second;
    long long c = include[i].second;
    tree[a].push_back({b, c});
    tree[b].push_back({a, c});
  }
  dfs(0, 0, 0, 0);
  for (int i = 0; i < q; i++) {
    int a = qs[i].first;
    int b = qs[i].second;
    cout << res(a, b) << "\n";
  }
  return 0;
}
