← Home
package main

import (
	"io"
	"os"
	"strconv"
)

type Edge struct {
	u, v int
	x    int64
}

type Block struct {
	edges     []int
	treeEdges []int
}

type Pair struct {
	to, eid int
}

type Rel struct {
	to     int
	nt, tr int
}

type Op struct {
	coef   int64
	tr, nt int
}

var (
	n, m   int
	mod    int64
	edges  []Edge
	adj    [][]int
	tin    []int
	low    []int
	timer  int
	estack []int
	comps  [][]int
)

func dfs(v, pe int) {
	timer++
	tin[v] = timer
	low[v] = timer
	for _, eid := range adj[v] {
		if eid == pe {
			continue
		}
		e := edges[eid]
		to := e.u
		if to == v {
			to = e.v
		}
		if tin[to] == 0 {
			estack = append(estack, eid)
			dfs(to, eid)
			if low[to] < low[v] {
				low[v] = low[to]
			}
			if low[to] >= tin[v] {
				comp := make([]int, 0)
				for {
					last := estack[len(estack)-1]
					estack = estack[:len(estack)-1]
					comp = append(comp, last)
					if last == eid {
						break
					}
				}
				comps = append(comps, comp)
			}
		} else if tin[to] < tin[v] {
			estack = append(estack, eid)
			if tin[to] < low[v] {
				low[v] = tin[to]
			}
		}
	}
}

func find(par []int, x int) int {
	for par[x] != x {
		par[x] = par[par[x]]
		x = par[x]
	}
	return x
}

func powMod(a, e, mod int64) int64 {
	res := int64(1)
	for e > 0 {
		if e&1 == 1 {
			res = res * a % mod
		}
		a = a * a % mod
		e >>= 1
	}
	return res
}

func main() {
	data, _ := io.ReadAll(os.Stdin)
	idx := 0
	nextInt := func() int64 {
		for idx < len(data) && data[idx] <= ' ' {
			idx++
		}
		sign := int64(1)
		if idx < len(data) && data[idx] == '-' {
			sign = -1
			idx++
		}
		var val int64
		for idx < len(data) && data[idx] > ' ' {
			val = val*10 + int64(data[idx]-'0')
			idx++
		}
		return val * sign
	}

	n = int(nextInt())
	m = int(nextInt())
	mod = nextInt()

	edges = make([]Edge, m)
	adj = make([][]int, n)
	for i := 0; i < m; i++ {
		u := int(nextInt()) - 1
		v := int(nextInt()) - 1
		x := nextInt() % mod
		edges[i] = Edge{u, v, x}
		adj[u] = append(adj[u], i)
		adj[v] = append(adj[v], i)
	}

	tin = make([]int, n)
	low = make([]int, n)
	for i := 0; i < n; i++ {
		if tin[i] == 0 {
			dfs(i, -1)
		}
	}

	blocks := make([]Block, len(comps))
	inBase := make([]bool, m)
	baseTree := make([]int, 0, n-1)

	markV := make([]int, n)
	markStamp := 0
	ufPar := make([]int, n)
	ufSz := make([]int, n)

	for bi, comp := range comps {
		blocks[bi].edges = comp
		markStamp++
		verts := make([]int, 0)
		for _, eid := range comp {
			u, v := edges[eid].u, edges[eid].v
			if markV[u] != markStamp {
				markV[u] = markStamp
				verts = append(verts, u)
			}
			if markV[v] != markStamp {
				markV[v] = markStamp
				verts = append(verts, v)
			}
		}
		for _, v := range verts {
			ufPar[v] = v
			ufSz[v] = 1
		}
		for _, eid := range comp {
			u, v := edges[eid].u, edges[eid].v
			ru := find(ufPar, u)
			rv := find(ufPar, v)
			if ru != rv {
				if ufSz[ru] < ufSz[rv] {
					ru, rv = rv, ru
				}
				ufPar[rv] = ru
				ufSz[ru] += ufSz[rv]
				blocks[bi].treeEdges = append(blocks[bi].treeEdges, eid)
				inBase[eid] = true
				baseTree = append(baseTree, eid)
			}
		}
	}

	if len(baseTree) != n-1 {
		os.Stdout.Write([]byte("-1\n"))
		return
	}

	possible := true
	alphaKnown := false
	alpha := int64(0)

	for _, block := range blocks {
		s := int64(0)
		for _, eid := range block.edges {
			s += edges[eid].x
			if s >= mod {
				s %= mod
			}
		}
		s %= mod
		r := int64(len(block.treeEdges)) % mod
		if r != 0 {
			cand := s * powMod(r, mod-2, mod) % mod
			if !alphaKnown {
				alphaKnown = true
				alpha = cand
			} else if alpha != cand {
				possible = false
				break
			}
		} else if s != 0 {
			possible = false
			break
		}
	}

	if !possible {
		os.Stdout.Write([]byte("-1\n"))
		return
	}

	if !alphaKnown {
		alpha = 0
	}

	for _, block := range blocks {
		s := int64(0)
		for _, eid := range block.edges {
			s += edges[eid].x
			if s >= mod {
				s %= mod
			}
		}
		s %= mod
		r := int64(len(block.treeEdges)) % mod
		if s != alpha*r%mod {
			possible = false
			break
		}
	}

	if !possible {
		os.Stdout.Write([]byte("-1\n"))
		return
	}

	baseCoef := alpha
	swapOps := make([]Op, 0)

	visV := make([]int, n)
	parentV := make([]int, n)
	parentE := make([]int, n)
	bfsStamp := 0

	for _, block := range blocks {
		if len(block.edges) <= 1 {
			continue
		}

		k := len(block.edges)
		idxMap := make(map[int]int, k)
		for i, eid := range block.edges {
			idxMap[eid] = i
		}

		treeAdj := make([][]Pair, n)
		for _, eid := range block.treeEdges {
			u, v := edges[eid].u, edges[eid].v
			treeAdj[u] = append(treeAdj[u], Pair{v, eid})
			treeAdj[v] = append(treeAdj[v], Pair{u, eid})
		}

		aux := make([][]Rel, k)
		for _, f := range block.edges {
			if inBase[f] {
				continue
			}
			u, v := edges[f].u, edges[f].v
			bfsStamp++
			q := make([]int, 1, len(block.treeEdges)+1)
			q[0] = u
			visV[u] = bfsStamp
			parentV[u] = -1
			for head := 0; head < len(q) && visV[v] != bfsStamp; head++ {
				cur := q[head]
				for _, pr := range treeAdj[cur] {
					to := pr.to
					if visV[to] == bfsStamp {
						continue
					}
					visV[to] = bfsStamp
					parentV[to] = cur
					parentE[to] = pr.eid
					q = append(q, to)
				}
			}
			if visV[v] != bfsStamp {
				possible = false
				break
			}
			a := idxMap[f]
			for cur := v; cur != u; cur = parentV[cur] {
				e := parentE[cur]
				b := idxMap[e]
				aux[a] = append(aux[a], Rel{b, f, e})
				aux[b] = append(aux[b], Rel{a, f, e})
			}
		}
		if !possible {
			break
		}

		par := make([]int, k)
		parNT := make([]int, k)
		parTR := make([]int, k)
		visE := make([]bool, k)
		order := make([]int, 0, k)

		q2 := make([]int, 1, k)
		q2[0] = 0
		visE[0] = true
		par[0] = -1

		for head := 0; head < len(q2); head++ {
			u := q2[head]
			order = append(order, u)
			for _, rel := range aux[u] {
				if visE[rel.to] {
					continue
				}
				visE[rel.to] = true
				par[rel.to] = u
				parNT[rel.to] = rel.nt
				parTR[rel.to] = rel.tr
				q2 = append(q2, rel.to)
			}
		}

		if len(order) != k {
			possible = false
			break
		}

		w := make([]int64, k)
		for i, eid := range block.edges {
			val := edges[eid].x
			if inBase[eid] {
				val -= alpha
				val %= mod
				if val < 0 {
					val += mod
				}
			}
			w[i] = val
		}

		for t := len(order) - 1; t >= 1; t-- {
			uidx := order[t]
			c := w[uidx]
			pidx := par[uidx]
			if c != 0 {
				nt := parNT[uidx]
				tr := parTR[uidx]
				ueid := block.edges[uidx]
				peid := block.edges[pidx]
				var s int64
				if ueid == nt && peid == tr {
					s = c
				} else if ueid == tr && peid == nt {
					s = mod - c
					if s == mod {
						s = 0
					}
				} else {
					possible = false
					break
				}
				if s != 0 {
					swapOps = append(swapOps, Op{s, tr, nt})
					baseCoef -= s
					baseCoef %= mod
					if baseCoef < 0 {
						baseCoef += mod
					}
				}
				w[pidx] += c
				if w[pidx] >= mod {
					w[pidx] -= mod
				}
			}
		}
		if !possible {
			break
		}
		if w[order[0]]%mod != 0 {
			possible = false
			break
		}
	}

	if !possible {
		os.Stdout.Write([]byte("-1\n"))
		return
	}

	posInBase := make([]int, m)
	for i := 0; i < m; i++ {
		posInBase[i] = -1
	}
	for i, eid := range baseTree {
		posInBase[eid] = i
	}

	allOps := make([]Op, 0, len(swapOps)+1)
	if baseCoef != 0 {
		allOps = append(allOps, Op{baseCoef, -1, -1})
	}
	allOps = append(allOps, swapOps...)

	out := make([]byte, 0, 1<<20)
	out = strconv.AppendInt(out, int64(len(allOps)), 10)
	out = append(out, '\n')
	for _, op := range allOps {
		out = strconv.AppendInt(out, op.coef, 10)
		pos := -1
		if op.tr != -1 {
			pos = posInBase[op.tr]
		}
		for i, eid := range baseTree {
			out = append(out, ' ')
			id := eid
			if i == pos {
				id = op.nt
			}
			out = strconv.AppendInt(out, int64(id+1), 10)
		}
		out = append(out, '\n')
	}

	os.Stdout.Write(out)
}