← Home
```go
package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
)

func main() {
	// Use buffered I/O for performance
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanWords)
	// Increase buffer size to handle potential large inputs
	buf := make([]byte, 2048*1024)
	scanner.Buffer(buf, 2048*1024)

	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	// Helper functions for scanning
	scanInt := func() int {
		scanner.Scan()
		x, _ := strconv.Atoi(scanner.Text())
		return x
	}
	scanInt64 := func() int64 {
		scanner.Scan()
		x, _ := strconv.ParseInt(scanner.Text(), 10, 64)
		return x
	}

	if !scanner.Scan() {
		return
	}
	t, _ := strconv.Atoi(scanner.Text())

	for i := 0; i < t; i++ {
		n := scanInt()
		m := scanInt()
		k := scanInt64()

		h := make([]int64, n)
		for j := 0; j < n; j++ {
			h[j] = scanInt64()
		}

		adj := make([][]int, n)
		hasParent := make([]bool, n)
		for j := 0; j < m; j++ {
			u := scanInt() - 1
			v := scanInt() - 1
			// The problem guarantees u < v, so no cycles and topological order is 0..n-1
			adj[u] = append(adj[u], v)
			hasParent[v] = true
		}

		dp := make([]int64, n)
		// Compute longest path in DAG starting from u
		// Since u < v for all edges, we iterate backwards from n-1 to 0
		for u := n - 1; u >= 0; u-- {
			var maxDist int64 = 0
			for _, v := range adj[u] {
				// Calculate wait time to satisfy h[v]
				dist := (h[v] - h[u] + k) % k
				if dist+dp[v] > maxDist {
					maxDist = dist + dp[v]
				}
			}
			dp[u] = maxDist
		}

		// Identify roots (nodes with in-degree 0) and group by their start hour h
		type Root struct {
			h   int64
			val int64 // This represents h[u] + dp[u], the completion time relative to start of day 0
		}
		
		rootMap := make(map[int64]int64)
		for u := 0; u < n; u++ {
			if !hasParent[u] {
				val := h[u] + dp[u]
				// If multiple roots have same h, we only care about the one that finishes latest
				if curr, exists := rootMap[h[u]]; !exists || val > curr {
					rootMap[h[u]] = val
				}
			}
		}

		roots := make([]Root, 0, len(rootMap))
		for rh, val := range rootMap {
			roots = append(roots, Root{h: rh, val: val})
		}

		// Sort roots by hour
		sort.Slice(roots, func(a, b int) bool {
			return roots[a].h < roots[b].h
		})

		if len(roots) == 0 {
			fmt.Fprintln(writer, 0)
			continue
		}

		p := len(roots)
		
		// Precompute suffix maximums of val
		sufMax := make([]int64, p)
		sufMax[p-1] = roots[p-1].val
		for j := p - 2; j >= 0; j-- {
			if roots[j].val > sufMax[j+1] {
				sufMax[j] = roots[j].val
			} else {
				sufMax[j] = sufMax[j+1]
			}
		}

		// Precompute prefix maximums of val
		preMaxVal := make([]int64, p)
		preMaxVal[0] = roots[0].val
		for j := 1; j < p; j++ {
			if roots[j].val > preMaxVal[j-1] {
				preMaxVal[j] = roots[j].val
			} else {
				preMaxVal[j] = preMaxVal[j-1]
			}
		}

		minDiff := int64(-1)

		// Try setting start of the day corresponding to each root's h
		// Effectively sliding a window of length k over the circular hours
		for j := 0; j < p; j++ {
			// If we align the "start" of our game such that roots[j] is the first processed root in the day:
			// Roots j...p-1 start at their h. Max end time is sufMax[j].
			// Roots 0...j-1 start at their h + k. Max end time is preMaxVal[j-1] + k.
			
			currMax := sufMax[j]
			if j > 0 {
				pm := preMaxVal[j-1] + k
				if pm > currMax {
					currMax = pm
				}
			}
			
			// Completion time is (Max End Time) - (Start Time of First Root)
			// Start Time of First Root is roots[j].h
			diff := currMax - roots[j].h
			
			if minDiff == -1 || diff < minDiff {
				minDiff = diff
			}
		}

		fmt.Fprintln(writer, minDiff)
	}
}
```