```go
package main
import (
"bufio"
"fmt"
"os"
)
// Global variables to store graph and state
var (
adj [][]Edge
priorities []int
ans []int
parent []int
counts []int
n int
)
// Edge represents a connection to a child/parent with capacity
type Edge struct {
to int
cap int
}
// Division represents a soldier division
type Division struct {
id int
priority int
time int
}
// DSU find with path compression
// Finds the next available time slot
func find(i int) int {
if parent[i] == i {
return i
}
parent[i] = find(parent[i])
return parent[i]
}
// DSU union
// Connects time i to j (usually i+1)
func union(i, j int) {
rootI := find(i)
rootJ := find(j)
if rootI != rootJ {
parent[rootI] = rootJ
}
}
func main() {
// Configure fast I/O
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
// Helper to read integers
readInt := func() int {
scanner.Scan()
s := scanner.Bytes()
x := 0
for _, b := range s {
x = x*10 + int(b-'0')
}
return x
}
if !scanner.Scan() {
return
}
// Parse n
s := scanner.Bytes()
n = 0
for _, b := range s {
n = n*10 + int(b-'0')
}
priorities = make([]int, n+1)
for i := 1; i <= n; i++ {
priorities[i] = readInt()
}
adj = make([][]Edge, n+1)
for i := 0; i < n-1; i++ {
u := readInt()
v := readInt()
c := readInt()
adj[u] = append(adj[u], Edge{to: v, cap: c})
adj[v] = append(adj[v], Edge{to: u, cap: c})
}
ans = make([]int, n+1)
// Pre-allocate DSU arrays
// Size is sufficient for max possible time (approx N + N)
// We use 2*N + 5000 to be safe.
parent = make([]int, 2*n+5000)
counts = make([]int, 2*n+5000)
// Perform DFS from the capital (city 1)
dfs(1, 0)
// Print results
out := bufio.NewWriter(os.Stdout)
for i := 1; i <= n; i++ {
if i > 1 {
out.WriteByte(' ')
}
fmt.Fprint(out, ans[i])
}
out.WriteByte('\n')
out.Flush()
}
// dfs processes the tree bottom-up.
// It returns a list of divisions currently at node u, sorted by priority.
// The 'time' field in the returned divisions represents the arrival time at u.
func dfs(u, p int) []Division {
// Division starting at current city u arrives at time 0
currentDiv := Division{id: u, priority: priorities[u], time: 0}
merged := []Division{currentDiv}
for _, e := range adj[u] {
v := e.to
if v == p {
continue
}
// Get divisions from the subtree at v
childList := dfs(v, u)
// Process movement from v to u
processedList := processEdge(childList, e.cap)
// Merge the processed list into the current node's list
merged = merge(merged, processedList)
}
// If we are at the capital, record the final arrival times
if u == 1 {
for _, d := range merged {
ans[d.id] = d.time
}
}
return merged
}
// processEdge computes the arrival times at the parent node given divisions at a child node.
// It simulates the capacity constraints of the edge.
func processEdge(divs []Division, capacity int) []Division {
// Find the range of time slots we might touch
maxT := 0
for _, d := range divs {
if d.time > maxT {
maxT = d.time
}
}
// Reset DSU and counts for the relevant range
// The range is up to maxT + number of divisions waiting + buffer
limit := maxT + len(divs) + 2
if limit >= len(parent) {
limit = len(parent) - 1
}
for i := 0; i <= limit; i++ {
parent[i] = i
counts[i] = 0
}
// Process divisions in priority order
for i := range divs {
// Find the earliest available departure time >= arrival time
t := find(divs[i].time)
// Division leaves at t, arrives at parent at t+1
divs[i].time = t + 1
// Update capacity usage for time t
counts[t]++
if counts[t] == capacity {
// If capacity full, link t to t+1 so next find skips t
union(t, t+1)
}
}
return divs
}
// merge combines two sorted slices of Divisions into one sorted slice
func merge(a, b []Division) []Division {
res := make([]Division, 0, len(a)+len(b))
i, j := 0, 0
for i < len(a) && j < len(b) {
if a[i].priority < b[j].priority {
res = append(res, a[i])
i++
} else {
res = append(res, b[j])
j++
}
}
res = append(res, a[i:]...)
res = append(res, b[j:]...)
return res
}
```