package main
import (
"io"
"os"
"strconv"
)
type Edge struct {
u int
v int
w int64
}
type DSU struct {
p []int
sz []int
}
func NewDSU(n int) *DSU {
p := make([]int, n)
sz := make([]int, n)
for i := 0; i < n; i++ {
p[i] = i
sz[i] = 1
}
return &DSU{p: p, sz: sz}
}
func (d *DSU) Find(x int) int {
for d.p[x] != x {
d.p[x] = d.p[d.p[x]]
x = d.p[x]
}
return x
}
func (d *DSU) Union(a, b int) bool {
ra := d.Find(a)
rb := d.Find(b)
if ra == rb {
return false
}
if d.sz[ra] < d.sz[rb] {
ra, rb = rb, ra
}
d.p[rb] = ra
d.sz[ra] += d.sz[rb]
return true
}
func main() {
data, _ := io.ReadAll(os.Stdin)
idx := 0
nextInt := func() int64 {
n := len(data)
for idx < n && data[idx] <= ' ' {
idx++
}
sign := int64(1)
if idx < n && data[idx] == '-' {
sign = -1
idx++
}
var val int64
for idx < n && data[idx] > ' ' {
val = val*10 + int64(data[idx]-'0')
idx++
}
return val * sign
}
n := int(nextInt())
color := make([]int, n)
whiteAll := make([]int, 0)
blackAll := make([]int, 0)
whiteIDs := make([]int, 0)
blackIDs := make([]int, 0)
whiteRem := make([]int64, 0)
blackRem := make([]int64, 0)
for i := 0; i < n; i++ {
c := int(nextInt())
s := nextInt()
color[i] = c
if c == 0 {
whiteAll = append(whiteAll, i)
if s > 0 {
whiteIDs = append(whiteIDs, i)
whiteRem = append(whiteRem, s)
}
} else {
blackAll = append(blackAll, i)
if s > 0 {
blackIDs = append(blackIDs, i)
blackRem = append(blackRem, s)
}
}
}
edges := make([]Edge, 0, n-1)
dsu := NewDSU(n)
i, j := 0, 0
for i < len(whiteIDs) && j < len(blackIDs) {
x := whiteRem[i]
if blackRem[j] < x {
x = blackRem[j]
}
u := whiteIDs[i]
v := blackIDs[j]
edges = append(edges, Edge{u: u, v: v, w: x})
dsu.Union(u, v)
whiteRem[i] -= x
blackRem[j] -= x
if whiteRem[i] == 0 {
i++
}
if blackRem[j] == 0 {
j++
}
}
repW := make([]int, n)
repB := make([]int, n)
used := make([]bool, n)
for k := 0; k < n; k++ {
repW[k] = -1
repB[k] = -1
}
roots := make([]int, 0)
for v := 0; v < n; v++ {
r := dsu.Find(v)
if !used[r] {
used[r] = true
roots = append(roots, r)
}
if color[v] == 0 {
if repW[r] == -1 {
repW[r] = v
}
} else {
if repB[r] == -1 {
repB[r] = v
}
}
}
root := -1
rootWhite := -1
rootBlack := -1
for _, r := range roots {
if repW[r] != -1 && repB[r] != -1 {
root = r
rootWhite = repW[r]
rootBlack = repB[r]
break
}
}
if root == -1 {
w0 := whiteAll[0]
b0 := blackAll[0]
for _, b := range blackAll {
edges = append(edges, Edge{u: w0, v: b, w: 0})
}
for _, w := range whiteAll {
if w != w0 {
edges = append(edges, Edge{u: w, v: b0, w: 0})
}
}
} else {
for _, r := range roots {
if r == root {
continue
}
if repW[r] != -1 {
edges = append(edges, Edge{u: repW[r], v: rootBlack, w: 0})
} else {
edges = append(edges, Edge{u: rootWhite, v: repB[r], w: 0})
}
}
}
out := make([]byte, 0, (n-1)*24)
for _, e := range edges {
out = strconv.AppendInt(out, int64(e.u+1), 10)
out = append(out, ' ')
out = strconv.AppendInt(out, int64(e.v+1), 10)
out = append(out, ' ')
out = strconv.AppendInt(out, e.w, 10)
out = append(out, '\n')
}
os.Stdout.Write(out)
}