CodeEff/ECCO huggingface dataset:
# Original solution
#!/usr/bin/env pypy3

import itertools


UNDEF = 0


def mex(s):
    for i in itertools.count(1):
        if i not in s:
            return i


def color_edges(n, edges):
    adj_list = [set() for _ in range(n)]
    for i, (a, b) in enumerate(edges):
        adj_list[a].add(i)
        adj_list[b].add(i)
    colors = [UNDEF for _ in range(n - 1)]
    for v in range(n):
        adj_es = adj_list[v]
        num_adj = len(adj_es)
        cur_cols = set(colors[i] for i in adj_es if colors[i] != UNDEF)
        new_cols = set()
        while len(cur_cols) < num_adj:
            nc = mex(cur_cols)
            cur_cols.add(nc)
            new_cols.add(nc)
        for u in adj_es:
            if colors[u] == UNDEF:
                colors[u] = new_cols.pop()
    return max(colors), colors


def main():
    n = int(input())
    edges = []
    for _ in range(n - 1):
        a, b = (int(z) - 1 for z in input().split())
        edges.append((a, b))
    k, cs = color_edges(n, edges)
    print(k)
    print(*cs, sep="\n")


if __name__ == "__main__":
    main()


# Optimized solution
#!/usr/bin/env pypy3

import itertools


UNDEF = 0


def color_edges(n, edges):
    adj_list = [set() for _ in range(n)]
    for i, (a, b) in enumerate(edges):
        adj_list[a].add(i)
        adj_list[b].add(i)
    colors = [UNDEF] * (n - 1)
    for v in range(n):
        adj_es = adj_list[v]
        num_adj = len(adj_es)
        cur_cols = set(colors[i] for i in adj_es if colors[i] != UNDEF)
        new_cols = set()
        for i in itertools.count(1):
            if len(cur_cols) + len(new_cols) >= num_adj:
                break
            elif i not in cur_cols:
                new_cols.add(i)
        for u in adj_es:
            if colors[u] == UNDEF:
                colors[u] = new_cols.pop()
    return max(colors), colors


def main():
    n = int(input())
    edges = []
    for _ in range(n - 1):
        a, b = (int(z) - 1 for z in input().split())
        edges.append((a, b))
    k, cs = color_edges(n, edges)
    print(k)
    print(*cs, sep="\n")


if __name__ == "__main__":
    main()
# Original solution
from collections import deque, OrderedDict

n = int(input())
edges = [tuple(map(int, input().split())) for _ in range(n-1)]
# print(edges)
tree = [[] for _ in range(n)]
# Zero-based index. グラフを陽に持つ。
edge_color = OrderedDict()  # 辺のタプルをキーにした順序付き辞書
for e in edges:
    a = e[0] - 1
    b = e[1] - 1
    tree[a].append(b)
    tree[b].append(a)
    edge_color[(min(a, b), max(a, b))] = 0

print(max(len(v) for v in tree))

seen = [False] * n  # 踏破
que = deque()
que_append = que.append
que_popleft = que.popleft
start = 0
seen[start] = True
que_append(start)
# ある頂点に接する辺にすでに塗られた色のセット。
v_based_color = [set() for _ in range(n)]
while len(que):
    v = que_popleft()
    color_i = 1  # One-based index.
    for adj in tree[v]:
        if seen[adj]:
            continue
        seen[adj] = True
        que_append(adj)
        while color_i in v_based_color[v]:
            # その頂点からの辺で使われていない色になるまで足す。
            color_i += 1
        edge_color[(min(v, adj), max(v, adj))] = color_i
        # 辺の両端の頂点が触れている色のセットに追加。
        v_based_color[v].add(color_i)
        v_based_color[adj].add(color_i)
        color_i += 1

{print(c) for c in edge_color.values()}


# Optimized solution
from collections import OrderedDict, deque


def main():
    n = int(eval(input()))
    edges = [tuple(map(int, input().split())) for _ in range(n - 1)]
    tree = [[] for _ in range(n)]  # こうしないとすべて同じリストObjになる.
    coloring = OrderedDict()
    for e in edges:
        a, b = e[0] - 1, e[1] - 1
        tree[a].append(b)
        tree[b].append(a)
        coloring[(a, b)] = 0

    q = deque()
    seen = [False] * n
    q.append(0)
    col_count_by_v = [set() for _ in range(n)]
    while len(q):
        new_v = q.popleft()
        seen[new_v] = True
        color_i = 1  # One-based index.
        for adj in tree[new_v]:
            if seen[adj]:
                continue
            q.append(adj)
            while color_i in col_count_by_v[new_v]:
                color_i += 1
            coloring[(new_v, adj)] = color_i
            col_count_by_v[new_v].add(color_i)
            col_count_by_v[adj].add(color_i)
            color_i += 1

    print((len(set(coloring.values()))))
    for c in list(coloring.values()):
        print(c)


if __name__ == '__main__':
    main()
# Original solution
n=int(eval(input()))

l=[[0 for i in range(n-1)]for j in range(n)]
m=[0]*n
t=[]
w=[]
for p in range(n-1):
    e = list(map(int, input().split()))
    w.append(e)

for i in range(n-1):
    a=w[i][0]-1
    b=w[i][1]-1
    m[a] +=1
    m[b] +=1
    k=0
    while True:

        if l[a][k]==0 and l[b][k]==0:
            l[a][k]=1
            l[b][k]=1
            break
        else:
            k +=1
    t.append(k+1)

c=max(m)
print(c)
for g in range(n-1):
    print((t[g]))


# Optimized solution
n=int(eval(input()))

l=[[] for j in range(n)]
m=[e for e in range(n-1)]
t=[]

for i in range(n-1):
    a, b = list(map(int, input().split()))
    a -=1
    b -=1

    buf = min(list(set(m)^set(l[a]+l[b])))
    l[a].append(buf)
    l[b].append(buf)
    """
    while True:
        if (k in l[a])==False and (k in l[b])==False:
            l[a].append(k)
            l[b].append(k)
            break
        else:
            k +=1
"""
    t.append(buf+1)

print((max(t)))
for g in range(n-1):
    print((t[g]))
# Original solution
from collections import defaultdict
from collections import deque
from collections import OrderedDict
import itertools
from sys import stdin
input = stdin.readline


def main():
  N = int(input())
  G = [[] for _ in range(N)]
  dic = {}
  for i in range(N-1):
    a, b = map(lambda x: int(x)-1, input().split())
    G[a].append(b)
    G[b].append(a)
    dic[(a, b)] = i
    dic[(b, a)] = i

  color = [0]*(N-1)
  upcol = [0]*(N)
  upcol[0] = None
  visited = set()
  q = deque([0])
  max_col = 0

  while len(q):

    now = q.popleft()
    visited.add(now)
    c = 0

    for next_ in G[now]:
      if next_ not in visited:
        c += 1
        if upcol[now] is None or not(upcol[now] == c):
          color[dic[(now, next_)]] = c
          upcol[next_] = c
        else:
          c += 1
          color[dic[(now, next_)]] = c
          upcol[next_] = c
        q.append(next_)

    max_col = max(max_col, c)

  print(max_col)
  print(*color, sep='\n')


if(__name__ == '__main__'):
  main()


# Optimized solution
from collections import deque
from sys import stdin
input = stdin.readline


def main():
  N = int(input())
  G = [[] for _ in range(N)]
  dic = {}
  for i in range(N-1):
    a, b = map(lambda x: int(x)-1, input().split())
    G[a].append(b)
    G[b].append(a)
    dic[(a, b)] = i
    dic[(b, a)] = i

  color = [0]*(N-1)
  upcol = [0]*(N)
  upcol[0] = None
  visited = set()
  q = deque([0])
  max_col = 0

  while len(q):

    now = q.popleft()
    visited.add(now)
    c = 0

    for next_ in G[now]:
      if next_ in visited:
        continue
      c += 1
      if upcol[now] == c:
        c += 1

      color[dic[(now, next_)]] = c
      upcol[next_] = c

      q.append(next_)

    max_col = max(max_col, c)

  print(max_col)
  print(*color, sep='\n')


if(__name__ == '__main__'):
  main()
# Original solution
from collections import deque

N = int(eval(input()))
edges = [[] for i in range(N)]
e_list = []

for i in range(N-1):
    a,b = list(map(int, input().split()))
    a -= 1
    b -= 1
    e_list.append((a,b))
    edges[a].append((b,i))
    edges[b].append((a,i))
ans = [0] * (N-1)

"""
bfs pattern
"""
q = deque()
next_v = edges[0]
for c, (u,i) in enumerate(next_v):
    q.append((u, i, c+1))
    ans[i] = c+1

while q:
    u_p, _, c_p = q.popleft()
    next_v = edges[u_p]
    cnt = 1
    for j, (u, i) in enumerate(next_v):
        # すでに彩色されている辺はダメ
        if ans[i] > 0:
            continue
        # 親と色がかぶったら+1
        if cnt == c_p:
            cnt += 1
        ans[i] = cnt
        q.append((u,i,cnt))
        
        cnt += 1

print((max(ans)))
for a in ans:
    print(a)

# Optimized solution
from collections import deque

N = int(eval(input()))
edges = [[] for i in range(N)]
e_list = []

for i in range(N-1):
    a,b = list(map(int, input().split()))
    a -= 1
    b -= 1
    e_list.append((a,b))
    edges[a].append((b,i))
    edges[b].append((a,i))
ans = [0] * (N-1)

"""
bfs pattern
"""
q = deque()
next_v = edges[0]
for c, (u,i) in enumerate(next_v):
    q.append((u, i, c+1))
    ans[i] = c+1

while q:
    u_p, _, c_p = q.popleft()
    next_v = edges[u_p]
    cnt = 1
    for u, i in next_v:
        # すでに彩色されている辺はダメ
        if ans[i] > 0:
            continue
        # 親と色がかぶったら+1
        if cnt == c_p:
            cnt += 1
        ans[i] = cnt
        q.append((u,i,cnt))
        
        cnt += 1

print((max(ans)))
for a in ans:
    print(a)
# Original solution
import sys
input = sys.stdin.readline
sys.setrecursionlimit(50000)

tree = {}
edges = []
colors = set()
N = 0

def readlines(n):
    for _ in range(n):
        a, b = list(map(int, input().split()))
        yield a, b

def to_tree(edges_, n):
    tree_ = {i:[] for i in range(1,n+1)}
    for a, b in edges_:
        tree_[a].append(b)
        tree_[b].append(a)
    return tree_

def suc(u, parent):
    for v in tree[u]:
        if (u, v) != (u, parent):
            yield u, v

def assign(assignment, u, parent):
    suc_edges = suc(u, parent)
    if not suc_edges:
        return

    lest_colors = colors - {assignment.get((parent, u))}
    for edge, color in zip(suc_edges, lest_colors):
        assignment[edge] = color
        v = edge[0] if edge[1] == u else edge[1]
        assign(assignment, v, u)

def main():
    global tree, edges, colors, N
    N = int(eval(input()))
    colors = set(range(1,N))
    edges = list(readlines(N-1))
    tree = to_tree(edges, N)
    ans = {}
    assign(ans, 1, None)
    print((max(ans.values())))
    for edge in edges:
        print((ans[edge]))

if __name__ == "__main__":
    main()

# Optimized solution
import sys
input = sys.stdin.readline
sys.setrecursionlimit(50000)

tree = {}
edges = []
colors = set()
N = 0

def readlines(n):
    for _ in range(n):
        a, b = list(map(int, input().split()))
        yield a, b

def to_tree(edges_, n):
    tree_ = {i:[] for i in range(1,n+1)}
    for a, b in edges_:
        tree_[a].append(b)
        tree_[b].append(a)
    return tree_

def suc(u, parent):
    for v in [v for v in tree[u] if (u,v) in edges and (u,v) != (u, parent)]:
        yield u, v

def assign(assignment, u, parent):
    suc_edges = suc(u, parent)
    if not suc_edges:
        return

    lest_colors = colors - {assignment.get((parent, u))}
    for edge, color in zip(suc_edges, lest_colors):
        assignment[edge] = color
        v = edge[0] if edge[1] == u else edge[1]
        assign(assignment, v, u)

def main():
    global tree, edges, colors, N
    N = int(eval(input()))
    colors = set(range(1,N))
    edges = list(readlines(N-1))
    tree = to_tree(edges, N)
    ans = {}
    assign(ans, 1, None)
    print((max(ans.values())))
    for edge in edges:
        print((ans[edge]))

if __name__ == "__main__":
    main()
# Original solution
import sys
input = sys.stdin.readline
 
N = int(eval(input()))
AB = [[int(i) for i in input().split()] for _ in range(N-1)]

# graph[i]: 頂点iの親と子の集合
graph = [[] for _ in range(N+1)]
for a,b in AB:
    graph[a].append(b)
    graph[b].append(a)

# 根
root = 1
# parent[i]: 頂点iの親
parent = [0]*(N+1)
# トポロジカル順序
order = []
# 未探索の頂点
stack = [root]

# dfsでparentを求める
while stack:
    # n: 探索する頂点
    n = stack.pop()
    order.append(n)
    for j in graph[n]:
        # jがnの親の場合
        if j == parent[n]:
            continue
        parent[j] = n
        stack.append(j)

# color[i]: 頂点iと親を繋ぐ辺の色
color = [-1] * (N+1)
# dfsで頂点iから出る辺の色を塗る
for i in order:
    # 既に使用した色
    ng = color[i]
    # 色
    c = 1
    for j in graph[i]:
        # jがiの親の場合(塗装済みなのでcontinue)
        if j == parent[i]:
            continue
        if c == ng:
            c += 1
        # 頂点iと頂点jの辺に色を塗る
        color[j] = c
        c += 1

# 入力順に辺の色を出力する
answer = [color[a] if parent[a]==b else color[b] for a,b in AB]
print((max(answer)))
print(("\n".join(map(str,answer))))

# Optimized solution
import sys
input = sys.stdin.readline
 
N = int(eval(input()))
AB = [[int(i) for i in input().split()] for _ in range(N-1)]

# graph[i]: 頂点iの親と子の集合
graph = [[] for _ in range(N+1)]
for a,b in AB:
    graph[a].append(b)
    graph[b].append(a)

# 根
root = 1
# parent[i]: 頂点iの親
parent = [0]*(N+1)
# トポロジカル順序(dfsの順番)
order = []
# 未探索の頂点
stack = [root]

# dfsでparentを求める
while stack:
    # n: 探索する頂点
    n = stack.pop()
    order.append(n)
    for j in graph[n]:
        # jがnの親の場合
        if j == parent[n]:
            continue
        parent[j] = n
        stack.append(j)

# color[i]: 頂点iと親を繋ぐ辺の色
color = [-1] * (N+1)
# dfsで頂点iから出る辺の色を塗る
for i in order:
    # 既に使用した色
    ng = color[i]
    # 色
    c = 1
    for j in graph[i]:
        # jがiの親の場合(塗装済みなのでcontinue)
        if j == parent[i]:
            continue
        if c == ng:
            c += 1
        # 頂点iと頂点jの辺に色を塗る
        color[j] = c
        c += 1

# 入力順に辺の色を出力する
answer = [color[a] if parent[a]==b else color[b] for a,b in AB]
print((max(answer)))
print(("\n".join(map(str,answer))))
# Original solution
# input
n = int(eval(input()))

edge = [[0, 0, 0] for i in range(n-1)]
for i in range(n-1):
    edge[i][0] ,edge[i][1] = list(map(int, list(input().split())))

# process
k = 0
pcolors = [0 for i in range(n)]
for i in range(n):
    color = 0
    for j in range(n-1):
        if edge[j][0] == i+1:
            color += 1
            if color == pcolors[i]: color += 1

            edge[j][2] = color
            pcolors[edge[j][1]-1] = color
            if k < color: k = color
    

# output
print(k)
for e in edge:
    print((e[2]))

# Optimized solution
import sys
sys.setrecursionlimit(10**7)

# input
n = int(eval(input()))

edges = [[] for i in range(n)]
for i in range(n-1):
    a ,b = list(map(int, list(input().split())))
    edges[a-1].append((b-1, i))

# process
ans = [0]*(n-1)

def f(a, c):
    color = 1
    for b, i in edges[a]:
        if color == c: color += 1
        ans[i] = color 
        f(b, color)
        color += 1

f(0, 0)

# output
print((max(ans)))
for i in ans:
    print(i)
# Original solution
import sys
sys.setrecursionlimit(10 ** 6)
# input = sys.stdin.readline    ####
int1 = lambda x: int(x) - 1
def II(): return int(eval(input()))

def MI(): return list(map(int, input().split()))
def MI1(): return list(map(int1, input().split()))

def LI(): return list(map(int, input().split()))
def LI1(): return list(map(int1, input().split()))
def LLI(rows_number): return [LI() for _ in range(rows_number)]

def printlist(lst, k='\n'): print((k.join(list(map(str, lst)))))
INF = float('inf')

from collections import deque

def solve():
    N = II()
    E = [[] for _ in range(N)]

    out = []
    for i in range(N-1):
        a, b = MI1()
        E[a].append(b)
        E[b].append(a)
        out.append((a, b))

    q = deque([(0, -1, -1)])
    memo = {}
    max_color = 0
    while len(q) > 0:
        current, v_pre, c_pre = q.popleft()
        color = 1
        for nv in E[current]:
            if nv == v_pre: continue
            if color == c_pre:
                color += 1
            q.append((nv, current, color))
            memo[(min(nv, current), max(nv, current))] = color
            max_color = max(max_color, color)
            color += 1
    # print(memo)

    print(max_color)
    for t in out:
        print((memo[t]))

if __name__ == '__main__':
    solve()


# Optimized solution
import sys
sys.setrecursionlimit(10 ** 6)
# input = sys.stdin.readline    ####
int1 = lambda x: int(x) - 1
def II(): return int(eval(input()))

def MI(): return list(map(int, input().split()))
def MI1(): return list(map(int1, input().split()))

def LI(): return list(map(int, input().split()))
def LI1(): return list(map(int1, input().split()))
def LLI(rows_number): return [LI() for _ in range(rows_number)]

def printlist(lst, k='\n'): print((k.join(list(map(str, lst)))))
INF = float('inf')

from collections import deque

def solve():
    N = II()
    E = [[] for _ in range(N)]

    for i in range(N-1):
        a, b = MI1()
        E[a].append((b, i))
        E[b].append((a, i))

    q = deque([(0, -1, -1)])
    ans = [0] * (N-1)
    while len(q) > 0:
        current, v_pre, c_pre = q.popleft()
        color = 1
        for (nv, idx) in E[current]:
            if nv == v_pre: continue
            if color == c_pre:
                color += 1
            q.append((nv, current, color))
            ans[idx] = color
            color += 1

    print((max(ans)))
    printlist(ans)

if __name__ == '__main__':
    solve()
# Original solution
n=int(eval(input()))
g = [[]for i in range(n)]
ab = [list(map(int,input().split()))for i in range(n-1)]
for i in range(n-1):
    g[ab[i][0]-1].append(ab[i][1]-1)
ans = [[-1 for i in range(n)]for j in range(n)]
banColor = [-1]*n
from queue import deque
q = deque([])
q.append(0)
ma = 0
while q:
    nord = q.popleft()
    offset = 0
    for idx,value in enumerate(g[nord]):
        if idx ==banColor[nord]:
            offset += 1
        ans[nord][value] = idx + offset
        banColor[value] = idx+offset
        q.append(value)
        ma = max(ma,idx+offset+1)
print(ma)
for i in range(n-1):
    print((ans[ab[i][0]-1][ab[i][1]-1]+1))

# Optimized solution
from queue import deque

n=int(eval(input()))
ab = [list(map(int,input().split()))for i in range(n-1)]
g = [[]for i in range(n)]
for i,j in ab:
    g[i-1].append(j-1)
ans = {}
banColor = [-1]*n

q = deque([])
q.append(0)
ma = 0

while q:
    nord = q.popleft()
    offset = 0
    for idx,value in enumerate(g[nord]):
        if idx ==banColor[nord]:
            offset += 1
        ans[(nord,value)]= idx + offset
        banColor[value] = idx+offset
        q.append(value)
        ma = max(ma,idx+offset+1)
print(ma)
for i,j in ab:
    print((ans[(i-1,j-1)]+1))
# Original solution
from collections import deque

n=int(eval(input()))
E=[[]for _ in range(n)]
L=[]
for i in range(n-1):
   a,b=list(map(int,input().split()))
   E[a-1].append(b-1)
   L.append(b-1)

que=deque([0])
ans=[0]*n
while que:
   s=que.pop()
   color=1
   for ni in E[s]:
      if color==ans[s]:
         color+=1
      ans[ni]=color
      color+=1
      que.append(ni)
print((max(ans)))
for ni in L:
   print((ans[ni]))

# Optimized solution
from collections import deque

n=int(eval(input()))
E=[[]for _ in range(n)]
L=[]
for i in range(n-1):
   a,b=list(map(int,input().split()))
   E[a-1].append(b-1)
   L.append(b-1)

que=deque([0])
ans=[0]*n
while que:
   s=que.popleft()
   color=1
   for ni in E[s]:
      if color==ans[s]:
         color+=1
      ans[ni]=color
      color+=1
      que.append(ni)
print((max(ans)))
for ni in L:
   print((ans[ni]))
# Original solution
from collections import deque

n=int(eval(input()))
E=[[]for _ in range(n)]
L=[]
for i in range(n-1):
   a,b=list(map(int,input().split()))
   E[a-1].append(b-1)
   L.append(b-1)

que=deque([0])
ans=[0]*n
while que:
   s=que.popleft()
   color=1
   for ni in E[s]:
      if color==ans[s]:
         color+=1
      ans[ni]=color
      color+=1
      que.append(ni)
print((max(ans)))
for ni in L:
   print((ans[ni]))


# Optimized solution
from collections import deque

n=int(eval(input()))
E=[[]for _ in range(n)]
L=[]
for i in range(n-1):
   a,b=list(map(int,input().split()))
   E[a-1].append(b-1)
   L.append(b-1)

que=deque([0])
ans=[0]*n
while que:
   s=que.pop()
   color=1
   for ni in E[s]:
      if color==ans[s]:
         color+=1
      ans[ni]=color
      color+=1
      que.append(ni)
print((max(ans)))
for i in L:
   print((ans[i]))
# Original solution
N = int(eval(input()))
V = [[] for _ in range(N)]
E = [[-1 for _ in range(N)] for _ in range(N)]
yobidasi = []
for i in range(N - 1):
	a, b = list(map(int, input().split()))
	V[a - 1].append(b - 1)
	V[b - 1].append(a - 1)
	yobidasi.append([a - 1, b - 1])
	
color_num = 0
for i in range(N):
	color_num = max(color_num, len(V[i]))

visited = [False for _ in range(N)]
Q = [[0, 0]]
print(color_num)

while Q:
	cur, parent_color = Q.pop(0)
	visited[cur] = True
	color_lst = [n for n in range(1, color_num + 1)]
	if parent_color != 0: 
		color_lst.pop(parent_color - 1)
	for adj in V[cur]:
		if visited[adj] == False:
			# print("paint")
			color = color_lst.pop(0)
			E[min(adj, cur)][max(adj, cur)] = color
			Q.append([adj, color])
			
for i in range(N - 1):
	print((E[yobidasi[i][0]][yobidasi[i][1]]))


# Optimized solution
N = int(eval(input()))
V = [[] for _ in range(N)]
E = dict()
yobidasi = []
for i in range(N - 1):
	a, b = list(map(int, input().split()))
	V[a - 1].append(b - 1)
	V[b - 1].append(a - 1)
	yobidasi.append([a - 1, b - 1])
	E[a - 1, b - 1] = -1
	
color_num = 0
for i in range(N):
	color_num = max(color_num, len(V[i]))

visited = [False for _ in range(N)]
Q = [[0, 0]]
print(color_num)

while Q:
	cur, parent_color = Q.pop(0)
	visited[cur] = True
	color_lst = [n for n in range(1, color_num + 1)]
	if parent_color != 0: 
		color_lst.pop(parent_color - 1)
	for adj in V[cur]:
		if visited[adj] == False:
			visited[adj] = True
			color = color_lst.pop(0)
			E[min(adj, cur), max(adj, cur)] = color
			Q.append([adj, color])
			
for i in range(N - 1):
	print((E[yobidasi[i][0], yobidasi[i][1]]))
# Original solution
# -*- coding: utf-8 -*-

import sys

def input(): return sys.stdin.readline().strip()
def list2d(a, b, c): return [[c] * b for i in range(a)]
def list3d(a, b, c, d): return [[[d] * c for j in range(b)] for i in range(a)]
def list4d(a, b, c, d, e): return [[[[e] * d for j in range(c)] for j in range(b)] for i in range(a)]
def ceil(x, y=1): return int(-(-x // y))
def INT(): return int(eval(input()))
def MAP(): return list(map(int, input().split()))
def LIST(N=None): return list(MAP()) if N is None else [INT() for i in range(N)]
def Yes(): print('Yes')
def No(): print('No')
def YES(): print('YES')
def NO(): print('NO')
sys.setrecursionlimit(10 ** 9)
INF = 10 ** 18
MOD = 10 ** 9 + 7

N = INT()
nodes = [[] for i in range(N)]
edges = {}
for i in range(N-1):
    a, b = MAP()
    a -= 1; b -= 1
    nodes[a].append(b)
    nodes[b].append(a)
    edges[(a, b)] = [i, -1]

mx = 0
used = [set() for i in range(N)]
ans = [0] * (N-1)
cnt = 0
for u in range(N):
    mx = max(mx, len(nodes[u]))
    for v in nodes[u]:
        a, b = min(u, v), max(u, v)
        if edges[(a, b)][1] == -1:
            color = 1
            while color in used[a] or color in used[b]:
                color += 1
            edges[(a, b)][1] = color
            used[a].add(color)
            used[b].add(color)
            i = edges[(a, b)][0]
            ans[i] = str(color)
            cnt += 1
            if cnt == N-1:
                break
    else:
        continue
    break
print(mx)
print(('\n'.join(ans)))


# Optimized solution
# -*- coding: utf-8 -*-

import sys

def input(): return sys.stdin.readline().strip()
def list2d(a, b, c): return [[c] * b for i in range(a)]
def list3d(a, b, c, d): return [[[d] * c for j in range(b)] for i in range(a)]
def list4d(a, b, c, d, e): return [[[[e] * d for j in range(c)] for j in range(b)] for i in range(a)]
def ceil(x, y=1): return int(-(-x // y))
def INT(): return int(eval(input()))
def MAP(): return list(map(int, input().split()))
def LIST(N=None): return list(MAP()) if N is None else [INT() for i in range(N)]
def Yes(): print('Yes')
def No(): print('No')
def YES(): print('YES')
def NO(): print('NO')
sys.setrecursionlimit(10 ** 9)
INF = 10 ** 18
MOD = 10 ** 9 + 7

N = INT()
nodes = [[] for i in range(N)]
edges = {}
for i in range(N-1):
    a, b = MAP()
    a -= 1; b -= 1
    nodes[a].append(b)
    nodes[b].append(a)
    edges[(a, b)] = [i, -1]

mx = 0
used = [set() for i in range(N)]
ans = [0] * (N-1)
cnt = 0
for u in range(N):
    mx = max(mx, len(nodes[u]))
    color = 1
    for v in nodes[u]:
        a, b = min(u, v), max(u, v)
        if edges[(a, b)][1] == -1:
            while color in used[a] or color in used[b]:
                color += 1
            edges[(a, b)][1] = color
            used[a].add(color)
            used[b].add(color)
            i = edges[(a, b)][0]
            ans[i] = str(color)
            color += 1
            cnt += 1
            if cnt == N-1:
                break
    else:
        continue
    break
print(mx)
print(('\n'.join(ans)))