n = gets.to_i

edges = []
g = Array.new(n){ [] }
(n-1).times do |i|
  a,b = gets.split
  a,b = a.to_i-1, b.to_i-1
  a, b = b, a if a > b # 辺は頂点 i < j の形で入れる。
  g[a] << b # 隣接リストに
  g[b] << a # 反対向きにも隣接リストに.無向グラフなので
  edges << [a,b] # 辺リストにいれる。辺が識別されてて、最後に辺リストから辺を回して色をとりだす。
end

colors = Hash.new() # Esge => color # 辺についてO(1)で使った色を見れる。O(1)はホントに?
colors_used = Array.new(n){Hash.new} # 頂点と色についてO(1)で使ったか確かめられる。
used = Array.new(n){false} # 頂点について探索済みかどうかO(1)でわかる。
que = [0] # 頂点0から見ていくので0が入っている # 探索待ちの未探索の頂点リスト

k = 0 # 答えとなる色の数
while !que.empty? do
  v = que.shift # queにある頂点を取り出す. 破壊的メソッドなので先頭は削除される。
  cur = 1 # 使ってない色がわからないので、とりあず色1から見ていく。
  g[v].each do |w| # vの隣接頂点wを見ていく。
    next if used[w] == true # 探索済みの頂点なら見ない。
    k = g[v].size if k < g[v].size  # 隣接する頂点数より色数が小さければ色数を更新
    v<w ? (i,j = v,w) : (i,j = w,v) # i < jの並びで頂点を捉える。
    # next if colors[[i,j]] != nil # nilでなければi-jの辺に色がついてて頂点jは探索済みで,次に。
    cur += 1 while colors_used[v][cur] == true # v周りの頂点で色をつかっていれば次の色に更新していく。塗る前に使ってない色にする。
    colors_used[v][cur] = true # vに色を使ったので記録する。
    colors_used[w][cur] = true # wに色を使ったので記録する。
    colors[[i,j]] = cur # 辺に使った色をいれる。
    que.push(w) #探索待ちの未探索頂点リストに頂点wを入れる。
  end
  used[v] = true # 頂点vを探索済みにする。
end

puts k
edges.each{ |e| puts colors[e] }