Problem:
Score : 500 points

Problem Statement

There are N towns numbered 1 to N and M roads. The i-th road connects Town A_i and Town B_i bidirectionally and has a length of C_i.

Takahashi will travel between these towns by car, passing through these roads. The fuel tank of his car can contain at most L liters of fuel, and one liter of fuel is consumed for each unit distance traveled. When visiting a town while traveling, he can full the tank (or choose not to do so). Travel that results in the tank becoming empty halfway on the road cannot be done.

Process the following Q queries:

The tank is now full. Find the minimum number of times he needs to full his tank while traveling from Town s_i to Town t_i. If Town t_i is unreachable, print -1.

Constraints

All values in input are integers.

2 \leq N \leq 300

0 \leq M \leq \frac{N(N-1)}{2}

1 \leq L \leq 10^9

1 \leq A_i, B_i \leq N

A_i \neq B_i

\left(A_i, B_i\right) \neq \left(A_j, B_j\right) (if i \neq j)

\left(A_i, B_i\right) \neq \left(B_j, A_j\right) (if i \neq j)

1 \leq C_i \leq 10^9

1 \leq Q \leq N\left(N-1\right)

1 \leq s_i, t_i \leq N

s_i \neq t_i

\left(s_i, t_i\right) \neq \left(s_j, t_j\right) (if i \neq j)

Input

Input is given from Standard Input in the following format:

N M L
A_1 B_1 C_1
:
A_M B_M C_M
Q
s_1 t_1
:
s_Q t_Q

Output

Print Q lines.

The i-th line should contain the minimum number of times the tank needs to be fulled while traveling from Town s_i to Town t_i. If Town t_i is unreachable, the line should contain -1 instead.

Sample Input 1

3 2 5
1 2 3
2 3 3
2
3 2
1 3

Sample Output 1

0
1

To travel from Town 3 to Town 2, we can use the second road to reach Town 2 without fueling the tank on the way.

To travel from Town 1 to Town 3, we can first use the first road to get to Town 2, full the tank, and use the second road to reach Town 3.

Sample Input 2

4 0 1
1
2 1

Sample Output 2

-1

There may be no road at all.

Sample Input 3

5 4 4
1 2 2
2 3 2
3 4 3
4 5 2
20
2 1
3 1
4 1
5 1
1 2
3 2
4 2
5 2
1 3
2 3
4 3
5 3
1 4
2 4
3 4
5 4
1 5
2 5
3 5
4 5

Sample Output 3

0
0
1
2
0
0
1
2
0
0
0
1
1
1
0
0
2
2
1
0

Solution in Go (1.14.1) (judge verdict: Time Limit Exceeded)

// https://atcoder.jp/contests/abc143/tasks/abc143_e

package main

import (
	"bufio"
	"container/heap"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of strconv.Atoi().
// If strconv.Atoi() returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		// str := strconv.FormatInt(A[i], 10)  // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m, l int
var A, B, C []int
var q int

var answers [300 + 5][300 + 5]int

const MAX_NODE_NUM = 300 + 5 // 最大ノード数
const INF_30 = 1 << 60       // dpテーブル初期化用

// var v, e int                   // v: 頂点数, e: 辺数
var G [MAX_NODE_NUM + 5][]Edge // G: 隣接リストによるグラフ
// var dp [MAX_NODE_NUM + 5]Vertex
// var flags [MAX_NODE_NUM + 5]bool

const (
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

func main() {
	n, m, l = ReadInt3()
	// v, e = n, m
	A, B, C = make([]int, m), make([]int, m), make([]int, m)
	for i := 0; i < m; i++ {
		a, b, c := ReadInt3()
		a--
		b--
		if l < c {
			continue
		}

		A[i], B[i], C[i] = a, b, c
		G[a] = append(G[a], Edge{to: b, cost: c})
		G[b] = append(G[b], Edge{to: a, cost: c})
	}

	// 前計算
	for i := 0; i < n; i++ {
		dp := Dijkstra(i, n, G[:n])
		for j := 0; j < n; j++ {
			if dp[j].times < INF_30 {
				answers[i][j] = dp[j].times
			} else {
				answers[i][j] = -1
			}
		}
	}

	q = ReadInt()
	for i := 0; i < q; i++ {
		s, t := ReadInt2()
		s--
		t--
		fmt.Println(answers[s][t])
	}
}

func Dijkstra(sid, n int, AG [][]Edge) []V {
	// データをすべて初期化
	dp, colors := InitAll(n)

	// 始点の設定（問題によっては複数始点もありうる）
	pq := InitStartPoint(sid, dp, colors)

	// アルゴリズム本体
	for pq.Len() > 0 {
		pop := pq.pop()
		colors[pop.id] = BLACK
		if Less(dp[pop.id], pop.v) {
			continue
		}

		// 次のノードへの遷移
		for _, e := range AG[pop.id] {
			if colors[e.to] == BLACK {
				continue
			}

			// 値の更新
			nv := GenNextV(pop, e, dp)

			if Less(nv, dp[e.to]) {
				dp[e.to] = nv
				pq.push(&Vertex{id: e.to, v: nv})
				colors[e.to] = GRAY
			}
		}
	}

	return dp
}

// DP value type
type V struct {
	// {{
	gas, times int
	// }}
}

// InitAll returns initialized dp and colors slices.
func InitAll(n int) (dp []V, colors []int) {
	dp, colors = make([]V, n), make([]int, n)
	for i := 0; i < n; i++ {
		dp[i] = DijkstraVInf()
		colors[i] = WHITE
	}

	return dp, colors
}

// DijkstraVInf returns a infinite value for DP.
func DijkstraVInf() V {
	// return V(-1)

	// {{
	return V{gas: -1, times: INF_BIT60}
	// }}
}

// InitStartPoint returns initialized priority queue, and update dp and colors slices.
// *This function update arguments (side effects).*
func InitStartPoint(sid int, dp []V, colors []int) *VertexPQ {
	pq := NewVertexPQ()

	// pq.push(&Vertex{id: sid, v: V(sid)})
	// dp[sid] = V(sid)
	// colors[sid] = GRAY

	// {{
	pq.push(&Vertex{id: sid, v: V{gas: l, times: 0}})
	dp[sid] = V{gas: l, times: 0}
	colors[sid] = GRAY
	// }}

	return pq
}

// Less returns whether l is strictly less than r.
// This function is also used by priority queue.
func Less(l, r V) bool {
	// return l > r

	// {{
	if l.times < r.times {
		return true
	} else if l.times > r.times {
		return false
	} else {
		return l.gas > r.gas
	}
	// }}
}

// GenNextV returns next value considered by transition.
func GenNextV(cv *Vertex, e Edge, dp []V) V {
	// _min := func(integers ...int) int {
	// 	m := integers[0]
	// 	for i, integer := range integers {
	// 		if i == 0 {
	// 			continue
	// 		}
	// 		if m > integer {
	// 			m = integer
	// 		}
	// 	}
	// 	return m
	// }

	// return V(_min(int(dp[cv.id]), e.to))

	// {{
	if l < e.cost {
		return DijkstraVInf()
	}

	if dp[cv.id].gas >= e.cost {
		return V{gas: dp[cv.id].gas - e.cost, times: dp[cv.id].times}
	}

	return V{gas: l - e.cost, times: dp[cv.id].times + 1}
	// }}
}

type Edge struct {
	to   int
	cost int
}

type Vertex struct {
	id int
	v  V
}
type VertexPQ []*Vertex

func NewVertexPQ() *VertexPQ {
	temp := make(VertexPQ, 0)
	pq := &temp
	heap.Init(pq)

	return pq
}
func (pq *VertexPQ) push(target *Vertex) {
	heap.Push(pq, target)
}
func (pq *VertexPQ) pop() *Vertex {
	return heap.Pop(pq).(*Vertex)
}

func (pq VertexPQ) Len() int { return len(pq) }
func (pq VertexPQ) Less(i, j int) bool {
	return Less(pq[i].v, pq[j].v)
}
func (pq VertexPQ) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}
func (pq *VertexPQ) Push(x interface{}) {
	item := x.(*Vertex)
	*pq = append(*pq, item)
}
func (pq *VertexPQ) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}

// type Edge struct {
// 	to   int
// 	cost int
// }

// type Vertex struct {
// 	pri            int
// 	id, gas, times int
// }
// type VertexPQ []*Vertex

// func (pq VertexPQ) Len() int { return len(pq) }
// func (pq VertexPQ) Less(i, j int) bool {
// 	if pq[i].times < pq[j].times {
// 		return true
// 	} else if pq[i].times > pq[j].times {
// 		return false
// 	} else {
// 		if pq[i].gas > pq[j].gas {
// 			return true
// 		} else {
// 			return false
// 		}
// 	}
// 	// return pq[i].pri < pq[j].pri
// } // <: ASC, >: DESC
// func (pq VertexPQ) Swap(i, j int) {
// 	pq[i], pq[j] = pq[j], pq[i]
// }
// func (pq *VertexPQ) Push(x interface{}) {
// 	item := x.(*Vertex)
// 	*pq = append(*pq, item)
// }
// func (pq *VertexPQ) Pop() interface{} {
// 	old := *pq
// 	n := len(old)
// 	item := old[n-1]
// 	*pq = old[0 : n-1]
// 	return item
// }

// // how to use
// // temp := make(VertexPQ, 0, 100000+1)
// // pq := &temp
// // heap.Init(pq)
// // heap.Push(pq, &Vertex{pri: intValue})
// // popped := heap.Pop(pq).(*Vertex)

// // 蟻本/AOJ
// // 隣接リストで辺を管理
// // node idは0-based

// const MAX_NODE_NUM = 300 + 5 // 最大ノード数
// const INF_30 = 1 << 60       // dpテーブル初期化用

// var v, e int                   // v: 頂点数, e: 辺数
// var G [MAX_NODE_NUM + 5][]Edge // G: 隣接リストによるグラフ
// var dp [MAX_NODE_NUM + 5]Vertex
// var flags [MAX_NODE_NUM + 5]bool

// // sidを始点としてダイクストラ法を実行
// // O(|E| * log|V|)
// func Dijkstra(sid int) {
// 	// 昇順で取り出せるpriority queue
// 	temp := make(VertexPQ, 0, 200000+5)
// 	pq := &temp
// 	heap.Init(pq)

// 	// 初期化
// 	for i := 0; i < v; i++ {
// 		dp[i] = Vertex{gas: -1, times: INF_30} // idはいらないはず
// 		flags[i] = false
// 	}
// 	dp[sid] = Vertex{gas: l, times: 0}
// 	heap.Push(pq, &Vertex{gas: dp[sid].gas, times: dp[sid].times, id: sid})

// 	for pq.Len() > 0 {
// 		vertex := heap.Pop(pq).(*Vertex)
// 		v := vertex.id

// 		if flags[v] {
// 			continue
// 		}
// 		flags[v] = true

// 		for _, e := range G[v] {
// 			// 補給無しでいけるかどうかチェック
// 			if dp[v].gas >= e.cost {
// 				// 補給無し
// 				if dp[e.to].times > dp[v].times {
// 					// 回数が小さいなら必ず更新
// 					dp[e.to].times = dp[v].times
// 					dp[e.to].gas = dp[v].gas - e.cost
// 					heap.Push(pq, &Vertex{gas: dp[e.to].gas, times: dp[e.to].times, id: e.to})
// 				} else if dp[e.to].times == dp[v].times && dp[e.to].gas < dp[v].gas-e.cost {
// 					// 回数が同じでもガソリンが多いなら更新
// 					dp[e.to].gas = dp[v].gas - e.cost
// 					heap.Push(pq, &Vertex{gas: dp[e.to].gas, times: dp[e.to].times, id: e.to})
// 				}
// 			} else {
// 				// 補給あり
// 				// 補給しても通れないならパス
// 				if l < e.cost {
// 					continue
// 				}

// 				if dp[e.to].times > dp[v].times+1 {
// 					// 回数が小さいなら必ず更新
// 					dp[e.to].times = dp[v].times + 1
// 					dp[e.to].gas = l - e.cost
// 					heap.Push(pq, &Vertex{gas: dp[e.to].gas, times: dp[e.to].times, id: e.to})
// 				} else if dp[e.to].times == dp[v].times+1 && dp[e.to].gas < l-e.cost {
// 					// 回数が同じでもガソリンが多いなら更新
// 					dp[e.to].gas = l - e.cost
// 					heap.Push(pq, &Vertex{gas: dp[e.to].gas, times: dp[e.to].times, id: e.to})
// 				}
// 			}
// 		}
// 	}
// }

// type Node struct {
// 	pri      int
// 	dist, id int
// }
// type NodePQ []*Node

// func (pq NodePQ) Len() int           { return len(pq) }
// func (pq NodePQ) Less(i, j int) bool { return pq[i].pri < pq[j].pri } // <: ASC, >: DESC
// func (pq NodePQ) Swap(i, j int) {
// 	pq[i], pq[j] = pq[j], pq[i]
// }
// func (pq *NodePQ) Push(x interface{}) {
// 	item := x.(*Node)
// 	*pq = append(*pq, item)
// }
// func (pq *NodePQ) Pop() interface{} {
// 	old := *pq
// 	n := len(old)
// 	item := old[n-1]
// 	*pq = old[0 : n-1]
// 	return item
// }

// // how to use
// // temp := make(NodePQ, 0, 100000+1)
// // pq := &temp
// // heap.Init(pq)
// // heap.Push(pq, &Node{pri: intValue})
// // popped := heap.Pop(pq).(*Node)

// // MODはとったか？
// // 遷移だけじゃなくて最後の最後でちゃんと取れよ？

// /*******************************************************************/