package main
import (
"bufio"
"io"
"os"
"runtime"
"sort"
)
type FastScanner struct {
data []byte
idx int
n int
}
func (fs *FastScanner) NextInt() int {
for fs.idx < fs.n {
c := fs.data[fs.idx]
if c >= '0' && c <= '9' {
break
}
fs.idx++
}
val := 0
for fs.idx < fs.n {
c := fs.data[fs.idx]
if c < '0' || c > '9' {
break
}
val = val*10 + int(c-'0')
fs.idx++
}
return val
}
func writeInt(w *bufio.Writer, x int) {
if x == 0 {
w.WriteByte('0')
return
}
var buf [20]byte
i := len(buf)
for x > 0 {
i--
buf[i] = byte('0' + x%10)
x /= 10
}
w.Write(buf[i:])
}
func main() {
data, _ := io.ReadAll(bufio.NewReaderSize(os.Stdin, 1<<20))
fs := FastScanner{data: data, n: len(data)}
n := fs.NextInt()
m := n - 1
head := make([]int, n+1)
for i := range head {
head[i] = -1
}
to := make([]int, 2*m)
next := make([]int, 2*m)
ec := 0
addEdge := func(a, b int) {
to[ec] = b
next[ec] = head[a]
head[a] = ec
ec++
}
for i := 0; i < m; i++ {
a := fs.NextInt()
b := fs.NextInt()
addEdge(a, b)
addEdge(b, a)
}
parent := make([]int, n+1)
order := make([]int, n)
stack := make([]int, n)
sp := 0
stack[sp] = 1
sp++
ol := 0
for sp > 0 {
v := stack[sp-1]
sp--
order[ol] = v
ol++
for e := head[v]; e != -1; e = next[e] {
u := to[e]
if u == parent[v] {
continue
}
parent[u] = v
stack[sp] = u
sp++
}
}
nodeType := make([]int, n+1)
subSize := make([]int, n+1)
terminal1 := make([]bool, n+2)
repVertex := make([]int, n+2)
typeSize := make([]int, n+2)
terminal1[1] = true
typeSize[1] = 1
typesList := make([]int, 1, n)
typesList[0] = 1
trieMap1 := make(map[uint64]int, n)
numTrie1 := 1
tmp := make([]int, 0, 16)
for i := n - 1; i >= 0; i-- {
v := order[i]
tmp = tmp[:0]
sz := 1
for e := head[v]; e != -1; e = next[e] {
u := to[e]
if parent[u] == v {
tmp = append(tmp, nodeType[u])
sz += subSize[u]
}
}
if len(tmp) > 1 {
sort.Ints(tmp)
}
cur := 1
for _, lab := range tmp {
key := (uint64(cur) << 32) | uint64(lab)
nxt, ok := trieMap1[key]
if !ok {
numTrie1++
nxt = numTrie1
trieMap1[key] = nxt
}
cur = nxt
}
nodeType[v] = cur
subSize[v] = sz
if cur == 1 {
if repVertex[1] == 0 {
repVertex[1] = v
}
} else if !terminal1[cur] {
terminal1[cur] = true
repVertex[cur] = v
typeSize[cur] = sz
typesList = append(typesList, cur)
} else if repVertex[cur] == 0 {
repVertex[cur] = v
}
}
sort.Slice(typesList, func(i, j int) bool {
a, b := typesList[i], typesList[j]
if typeSize[a] != typeSize[b] {
return typeSize[a] < typeSize[b]
}
return a < b
})
typeCount := len(typesList)
rankOfType := make([]int, numTrie1+1)
rankWeight := make([]int, typeCount+1)
rankToType := make([]int, typeCount+1)
for i, t := range typesList {
r := i + 1
rankOfType[t] = r
rankWeight[r] = typeSize[t]
rankToType[r] = t
}
fs.data = nil
data = nil
trieMap1 = nil
terminal1 = nil
subSize = nil
order = nil
stack = nil
typeSize = nil
typesList = nil
runtime.GC()
trieParent2 := make([]int, n+2)
inLabel2 := make([]int, n+2)
dist2 := make([]int, n+2)
terminal2 := make([]bool, n+2)
head2 := make([]int, n+2)
for i := range head2 {
head2[i] = -1
}
edgeLabel2 := make([]int, n+1)
edgeNext2 := make([]int, n+1)
edgeCnt2 := 0
trieMap2 := make(map[uint64]int, n)
numTrie2 := 1
tmp2 := make([]int, 0, 16)
for v := 1; v <= n; v++ {
tmp2 = tmp2[:0]
for e := head[v]; e != -1; e = next[e] {
u := to[e]
if parent[u] == v {
tmp2 = append(tmp2, rankOfType[nodeType[u]])
}
}
if len(tmp2) > 1 {
sort.Ints(tmp2)
}
cur := 1
for _, lab := range tmp2 {
key := (uint64(cur) << 32) | uint64(lab)
nxt, ok := trieMap2[key]
if !ok {
numTrie2++
nxt = numTrie2
trieMap2[key] = nxt
trieParent2[nxt] = cur
inLabel2[nxt] = lab
dist2[nxt] = dist2[cur] + rankWeight[lab]
edgeLabel2[edgeCnt2] = lab
edgeNext2[edgeCnt2] = head2[cur]
head2[cur] = edgeCnt2
edgeCnt2++
}
cur = nxt
}
terminal2[cur] = true
}
bestCost := n + 1
bestNode := 1
bestExtra := 0
labbuf := make([]int, 0, 16)
for u := 1; u <= numTrie2; u++ {
if !terminal2[u] && dist2[u] < bestCost {
bestCost = dist2[u]
bestNode = u
bestExtra = 0
}
labbuf = labbuf[:0]
for e := head2[u]; e != -1; e = edgeNext2[e] {
labbuf = append(labbuf, edgeLabel2[e])
}
if len(labbuf) > 1 {
sort.Ints(labbuf)
}
expected := 1
if u != 1 {
expected = inLabel2[u]
}
for _, lab := range labbuf {
if lab < expected {
continue
}
if lab == expected {
expected++
} else {
break
}
}
if expected <= typeCount {
cand := dist2[u] + rankWeight[expected]
if cand < bestCost {
bestCost = cand
bestNode = u
bestExtra = expected
}
}
}
out := bufio.NewWriterSize(os.Stdout, 1<<20)
writeEdge := func(a, b int) {
writeInt(out, a)
out.WriteByte(' ')
writeInt(out, b)
out.WriteByte('\n')
}
if bestCost >= n {
for v := 2; v <= n; v++ {
writeEdge(parent[v], v)
}
out.Flush()
return
}
seq := make([]int, 0, 16)
x := bestNode
for x != 1 {
seq = append(seq, inLabel2[x])
x = trieParent2[x]
}
for i, j := 0, len(seq)-1; i < j; i, j = i+1, j-1 {
seq[i], seq[j] = seq[j], seq[i]
}
if bestExtra != 0 {
seq = append(seq, bestExtra)
}
badNodes := 0
for _, r := range seq {
badNodes += rankWeight[r]
}
chainLen := n - 1 - badNodes
for i := 1; i <= chainLen; i++ {
writeEdge(i, i+1)
}
bottom := chainLen + 1
curLabel := bottom
stackOrig := make([]int, 0, 16)
stackNew := make([]int, 0, 16)
for _, r := range seq {
t := rankToType[r]
rep := repVertex[t]
curLabel++
rootNew := curLabel
writeEdge(bottom, rootNew)
stackOrig = stackOrig[:0]
stackNew = stackNew[:0]
stackOrig = append(stackOrig, rep)
stackNew = append(stackNew, rootNew)
for len(stackOrig) > 0 {
o := stackOrig[len(stackOrig)-1]
stackOrig = stackOrig[:len(stackOrig)-1]
nn := stackNew[len(stackNew)-1]
stackNew = stackNew[:len(stackNew)-1]
for e := head[o]; e != -1; e = next[e] {
c := to[e]
if parent[c] == o {
curLabel++
cn := curLabel
writeEdge(nn, cn)
stackOrig = append(stackOrig, c)
stackNew = append(stackNew, cn)
}
}
}
}
out.Flush()
}