#include <bits/stdc++.h>
#define SORT(x) sort((x).begin(),(x).end())
#define ALL(x) (x).begin(),(x).end()
#define rep(i, n) for(int i = 0; i < n; i++)
#define reps(i, m, n) for(int i = m; i < n; i++)
#define repr(i, m, n) for(int i = m; i >= n; i--)
#define db(x) cout<<#x<<"="<<x<<endl
template<class T>bool maxi(T &a,const T &b){if(a<b){ a=b; return 1;} return 0;}
template<class T>bool mini(T &a,const T &b){if(b<a){ a=b; return 1;} return 0;}
#define y0 y3487465 //y1 j0 j1
#define INF2 1000000000000000037
#define INF 1000000007
#define PI (acos(-1))
#define MOD 1000000007
#define EPS 1e(-9);
using namespace std;
typedef long long ll;

ll L;

/*
struct edge{ll to; ll cost;};
struct graph{
    ll N; //ノードの個数
    vector<vector<edge> > G;
    vector<ll> parent;
    vector<ll> d1;
    vector<bool> used;

    graph(ll n){
        N = n;
        G.resize(N);
        parent.resize(N);
        d1.resize(N); //最小深さ
        used.resize(N); //現在の探索経路上にあるか
        rep(i,N){d1[i] = INF2;}
        rep(i,N){used[i] = false;}
    }
    void add_edge(ll s, ll t, ll cost){
        G[s].emplace_back((edge){t,cost});
    }

    void dfs(ll v, ll d1n, ll d2n){
        mini(d1[v],d1n);
        used[v]=true;
        for(auto E : G[v]){ // E つなぐエッジ
            if(used[E.to] == false){
                if(d2n-E.cost>=0) dfs(E.to , d1n, d2n-E.cost); //まだ行ってないなら
                else{
                    dfs(E.to , d1n+1, L-E.cost);
                }
            }
        }
        used[v]=false; //同じところに違う経路で行くなら追加
    }
};
*/

struct edge{ll to, cost;}; //edgeパラメータ
typedef pair<ll,ll> P;
struct graph{
  ll N; //ノードの個数
  vector<vector<edge> > G; //G[辺の元]((.to辺の先,.costコスト),...)
  vector<ll> d; // dist[n] = ノードnまでのの重み
  vector<ll> parent;

  graph(ll n){ //グラフ生成時
    N = n;
    G.resize(N); //Edge情報
    d.resize(N); //Node情報,ans
    parent.resize(N);
    rep(i,N){d[i] = INF2;}
  }

  void add_edge(ll s, ll t, ll cost){
    G[s].emplace_back((edge){t, cost});
  }

  void dijkstra(ll s){
    d[s] = 0;
    priority_queue<P,vector<P>, greater<P> > que; //ansが小さい順に出る
    que.push(P(0,s)); // P < ans , Node >
    while(!que.empty()){
      P p = que.top(); que.pop(); //pに <ans,Node> 入れる
      ll v = p.second; //vに Node 入れる
      if(d[v] < p.first) continue; //すでに良い数値が入っているならパス
      rep(i,G[v].size()){
        edge& E = G[v][i];
        ll buff=d[v]+E.cost;
        if(buff%(L*10)>L) buff=(buff/(L*10)+1)*(L*10)+E.cost;
        if(d[E.to] > buff){ //更新するなら
          d[E.to] = buff;
          que.push(P(d[E.to], E.to));
        }
      }
    }
  }
};

void Main(){

    ll N,M; cin>>N>>M>>L;

    graph g(N);

    rep(i,M){  //変える
        int a,b,c; cin>>a>>b>>c; --a; --b;
        if(c>L) continue;
        g.add_edge(a,b,c);
        g.add_edge(b,a,c);
    }

    ll ans[N][N];
    rep(i,N){
        rep(j,N){
            ans[i][j]=INF2;
        }
    }

    rep(i,N){
        g.dijkstra(i);
        rep(j,N){
            mini(ans[i][j],g.d[j]/(L*10));
        }
        rep(j,N){
            g.d[j]=INF2;
        }
    }

    int Q; cin>>Q;

    rep(i,Q){
        int s,t; cin>>s>>t; --s; --t;
        if(ans[s][t]==INF2/(L*10)){
            cout<< -1 <<"\n"; continue;
        }
        cout<< ans[s][t] <<"\n";
    }
}

//-----------------------------------
int main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout << fixed << setprecision(15);
    Main();
}
//-----------------------------------


Measured memory_bytes: 3584