#[allow(unused_macros)]
macro_rules! input {
  ($($r:tt)*) => {
    let stdin = std::io::stdin();
    let mut bytes = std::io::Read::bytes(std::io::BufReader::new(stdin.lock()));
    let mut next = move || -> String{
      bytes
        .by_ref()
        .map(|r|r.unwrap() as char)
        .skip_while(|c|c.is_whitespace())
        .take_while(|c|!c.is_whitespace())
        .collect()
    };
    input_inner!{next, $($r)*}
  };
}

#[allow(unused_macros)]
macro_rules! input_inner {
  ($next:expr) => {};
  ($next:expr, ) => {};

  ($next:expr, $var:ident : $t:tt $($r:tt)*) => {
    #[allow(unused_mut)]
    let mut $var = read_value!($next, $t);
    input_inner!{$next $($r)*}
  };
}

#[allow(unused_macros)]
macro_rules! read_value {
  ($next:expr, ( $($t:tt),* )) => {
    ( $(read_value!($next, $t)),* )
  };

  ($next:expr, [ $t:tt ; $len:expr ]) => {
    (0..$len).map(|_| read_value!($next, $t)).collect::<Vec<_>>()
  };

  ($next:expr, chars) => {
    read_value!($next, String).chars().collect::<Vec<char>>()
  };

  ($next:expr, usize1) => {
    read_value!($next, usize) - 1
  };

  ($next:expr, [ $t:tt ]) => {{
    let len = read_value!($next, usize);
    (0..len).map(|_| read_value!($next, $t)).collect::<Vec<_>>()
  }};

  ($next:expr, $t:ty) => {
    $next().parse::<$t>().expect("Parse error")
  };
}

fn main() {
  let stack_size = 104_857_600;
  let thd = std::thread::Builder::new().stack_size(stack_size);
  thd.spawn(|| work()).unwrap().join().unwrap();
}

use std::cmp::*;
use std::io::*;

#[derive(Eq,Debug)]
struct HeapNode {
  x: u32,
  y: u32,
  p: usize,
}

impl HeapNode {
  fn new(x_: u32, y_: u32, p_: usize) -> HeapNode {
    HeapNode {
      x: x_,
      y: y_,
      p: p_,
    }
  }
}

impl Ord for HeapNode {
  fn cmp(&self, other: &Self) -> Ordering {
    (other.x, other.y, other.p).cmp(&(self.x, self.y, self.p))
  }
}

impl PartialOrd for HeapNode {
  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
    Some(self.cmp(other))
  }
}

impl PartialEq for HeapNode {
  fn eq(&self, other: &Self) -> bool {
    self.cmp(other) == Ordering::Equal
  }
}

fn dij(limit: u32, mat: &Vec<Vec<Option<u32>>>, start: usize) -> Vec<Option<(u32, u32)>> {
  let n = mat.len();
  let mut heap = std::collections::BinaryHeap::new();
  let mut dis = vec![None; n];
  heap.push(HeapNode::new(0, 0, start));
  let mut cnt = 0;
  while !heap.is_empty() {
    if cnt >= n {
      break;
    }
    let node = heap.pop().unwrap();
    let p = node.p;
    if dis[p].is_some() {
      continue;
    }
    let d1 = node.x;
    let d2 = node.y;
    dis[p] = Some((d1, d2));
    cnt += 1;
    for j in 0..n {
      if dis[j].is_some() || mat[p][j].is_none() {
        continue;
      }
      let d = mat[p][j].unwrap();
      if d2 + d <= limit {
        heap.push(HeapNode::new(d1, d2 + d, j));
      } else {
        heap.push(HeapNode::new(d1 + 1, d, j));
      }
    }
  }
  dis
}

fn work() {
  let out = stdout();
  let mut out = BufWriter::new(out.lock());
  macro_rules! puts {
    ($($format:tt)*) => (write!(out,$($format)*).unwrap());
  }
  input! {
    n: usize,
    m: usize,
    limit: u32,
    es: [(usize, usize, u32); m],
    q: u32,
    qs: [(usize, usize); q],
  };
  let mut mat = vec![vec![None; n]; n];
  for &(a, b, c) in &es {
    if c <= limit {
      mat[a - 1][b - 1] = Some(c);
      mat[b - 1][a - 1] = Some(c);
    }
  }
  let mut ans = Vec::with_capacity(n);
  for i in 0..n {
    ans.push(dij(limit, &mat, i));
  }
  for &(x, y) in &qs {
    puts!("{}\n", match ans[x - 1][y - 1] {
      Some(t) => t.0 as i64,
      _ => -1
    });
  }
}


Measured memory_bytes: 14708