```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)
}
}
```