#pragma GCC optimize("Ofast")
#define _USE_MATH_DEFINES
#include "bits/stdc++.h"

using namespace std;

using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;

constexpr char newl = '\n';
constexpr double eps = 1e-10;

#define FOR(i,a,b) for (int i = (a); i < (b); i++)
#define F0R(i,b) FOR(i,0,b)
#define RFO(i,a,b) for (int i = ((b)-1); i >=(a); i--)
#define RF0(i,b) RFO(i,0,b)
#define fi first
#define se second
#define debug(x) cout << #x << ": " << x << '\n';
#define rng(a) a.begin(),a.end()
#define rrng(a) a.rbegin(),a.rend()

template<typename T1, typename T2> inline void chmin(T1& a, T2 b) { if (a > b) a = b; }
template<typename T1, typename T2> inline void chmax(T1& a, T2 b) { if (a < b) a = b; }
template<class T> void Print(vector<T> v) {
  F0R(i, v.size()) {
    cout << v[i] << ' ';
  }
  cout << newl;
}

#if 1

template <class T>
vector<T> Dijkstra(int v, const vector<vector<pair<int, T>>>& G) {

  int n = G.size();
  vector<T> d(n, numeric_limits<T>::max());
  d[v] = 0;

  auto cmp = [](const pair<int, T>& l, const pair<int, T>& r) {
    return l.second > r.second;
  };

  priority_queue<pair<int, T>, vector<pair<int, T>>, decltype(cmp)> qu{ cmp };
  qu.emplace(v, 0);

  while (qu.size()) {
    auto p = qu.top();
    qu.pop();
    if (d[p.first] < p.second) continue;

    for (auto cp : G[p.first]) {
      T alt = d[p.first] + cp.second;
      if (d[cp.first] > alt) {
        d[cp.first] = alt;
        //set prev
        qu.emplace(cp.first, alt);
      }
    }
  }

  return move(d);
}

int L;

struct Route {
  int a, b;
  Route(int a = 99999, int b = 0) : a(a), b(b) {

  }
  bool operator < (const Route& r) const {
    return a != r.a ? a < r.a : b < r.b;
  }
  bool operator > (const Route& r) const {
    return r < *this;
  }
  Route& operator += (const Route& r) {
    //cout << *this << " + " << r;
    a += r.a;
    b += r.b;
    if (b > L) {
      a++;
      b = r.b;
    }
    //cout << " = " << *this << newl;
    return *this;
  }
  Route operator + (const Route& r) {
    return Route(*this) += r;
  }
  friend ostream& operator << (ostream& os, const Route& x) noexcept {
    return os << x.a << "," << x.b;
  }
};

// INSERT ABOVE HERE
signed main() {
  cin.tie(0);
  ios_base::sync_with_stdio(false);

  /*
  以下、燃料タンクの容量を10として考える

  ルート1: 5 5 5 5 (距離20 補給1回)
  ルート2: 6 6 6 (距離18 補給2回)
  必ずしも距離が短いほど良いわけではない

  A→B: 10 5 (補給1回)
  B→C: 5 10 (補給1回)
  B→D: 10 10 (補給1回)
  B→C,B→Dはともに補給1回で行けるが、A→CとA→Dでは補給回数が異なる
  ルートの良さを記憶する際に、補給回数だけでは足りない

  よって、ルートを表すとき
  4 8 3 2 9 3 2
  これを 距離4 + 補給4 + 距離5 と表すことを考える

  以下略

  Dijkstraなら補給+距離で行けるのではないか
  */

  int N, M;
  cin >> N >> M >> L;
  vector<vector<pair<int, Route>>> G(N);
  F0R(i, M) {
    int a, b, c;
    cin >> a >> b >> c;
    if (c > L) continue;
    a--; b--;
    G[a].emplace_back(b, Route(0, c));
    G[b].emplace_back(a, Route(0, c));
  }

  vector<vector<Route>> rs(N);

  int Q;
  cin >> Q;
  F0R(i, Q) {
    int s, t;
    cin >> s >> t;
    s--; t--;

    if (rs[s].size() == 0) {
      rs[s] = Dijkstra(s, G);
    }
    int result = rs[s][t].a;

    cout << (result > 10000 ? -1 : result) << newl;
  }
}
#endif


Measured memory_bytes: 3752