```go
package main
import (
"bufio"
"fmt"
"os"
)
type Edge struct {
to, idx int
}
func main() {
reader := bufio.NewReader(os.Stdin)
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
var t int
if _, err := fmt.Fscan(reader, &t); err != nil {
return
}
for tc := 0; tc < t; tc++ {
var n int
fmt.Fscan(reader, &n)
adj := make([][]Edge, n+1)
for i := 1; i < n; i++ {
var u, v int
fmt.Fscan(reader, &u, &v)
adj[u] = append(adj[u], Edge{v, i})
adj[v] = append(adj[v], Edge{u, i})
}
children := make([][]Edge, n+1)
var buildDirTree func(u, p int)
buildDirTree = func(u, p int) {
for _, edge := range adj[u] {
if edge.to != p {
children[u] = append(children[u], edge)
buildDirTree(edge.to, u)
}
}
}
buildDirTree(1, 0)
root := 1
check := func(r int) bool {
var dfs func(u int) int
possible := true
dfs = func(u int) int {
if !possible {
return 0
}
W := make([]int, len(children[u]))
sum := 0
for i, child := range children[u] {
W[i] = dfs(child.to) + 1
if !possible {
return 0
}
sum += W[i]
}
dp := make([]bool, r+1)
dp[0] = true
for _, w := range W {
for j := r; j >= w; j-- {
if dp[j-w] {
dp[j] = true
}
}
}
maxB := 0
for j := r; j >= 0; j-- {
if dp[j] {
maxB = j
break
}
}
f_u := sum - maxB
if u == root {
if f_u > r {
possible = false
}
} else {
if f_u > r-1 {
possible = false
}
}
return f_u
}
dfs(root)
return possible
}
left, right := 1, n-1
ansR := n - 1
for left <= right {
mid := (left + right) / 2
if check(mid) {
ansR = mid
right = mid - 1
} else {
left = mid + 1
}
}
f := make([]int, n+1)
inB := make([][]bool, n+1)
var calcF func(u int)
calcF = func(u int) {
k := len(children[u])
W := make([]int, k)
for i, child := range children[u] {
calcF(child.to)
W[i] = f[child.to] + 1
}
dp := make([][]bool, k+1)
for i := range dp {
dp[i] = make([]bool, ansR+1)
}
dp[0][0] = true
for i := 0; i < k; i++ {
w := W[i]
for j := 0; j <= ansR; j++ {
if dp[i][j] {
dp[i+1][j] = true
if j+w <= ansR {
dp[i+1][j+w] = true
}
}
}
}
maxB := 0
for j := ansR; j >= 0; j-- {
if dp[k][j] {
maxB = j
break
}
}
sum := 0
for _, w := range W {
sum += w
}
f[u] = sum - maxB
in_B := make([]bool, k)
curr := maxB
for i := k - 1; i >= 0; i-- {
w := W[i]
if dp[i][curr] {
in_B[i] = false
} else {
in_B[i] = true
curr -= w
}
}
inB[u] = in_B
}
calcF(root)
ans := make([]int, n)
colorCounter := 0
var assign func(u int, parent_color int)
assign = func(u int, parent_color int) {
var new_color int
hasB := false
for i := range children[u] {
if inB[u][i] {
hasB = true
break
}
}
if hasB {
colorCounter++
new_color = colorCounter
}
for i, child := range children[u] {
v := child.to
idx := child.idx
if inB[u][i] {
ans[idx] = new_color
assign(v, new_color)
} else {
ans[idx] = parent_color
assign(v, parent_color)
}
}
}
assignRoot := func() {
var colorA, colorB int
hasA, hasB := false, false
for i := range children[root] {
if inB[root][i] {
hasB = true
} else {
hasA = true
}
}
if hasA {
colorCounter++
colorA = colorCounter
}
if hasB {
colorCounter++
colorB = colorCounter
}
for i, child := range children[root] {
v := child.to
idx := child.idx
if inB[root][i] {
ans[idx] = colorB
assign(v, colorB)
} else {
ans[idx] = colorA
assign(v, colorA)
}
}
}
assignRoot()
fmt.Fprintln(writer, ansR)
for i := 1; i < n; i++ {
if i > 1 {
fmt.Fprint(writer, " ")
}
fmt.Fprint(writer, ans[i])
}
fmt.Fprintln(writer)
}
}
```