#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <functional>
#include <iomanip>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <stack>
#include <queue>
#include <deque>
#include <bitset>
#include <array>
#include <list>
#include <forward_list>
#include <complex>
#include <numeric>
#include <regex>
#include <boost/optional.hpp>
//#include <boost/multiprecision/cpp_int.hpp>
//#include <boost/range/irange.hpp>

using namespace std;

//#define int Int
#define let auto const&
#define var decltype(auto)
#define ref auto&&

using Int = std::int_fast64_t;
using Uint = std::uint_fast64_t;

using ll = long long;
using ld = long double;

template<typename T=int> using vec = std::vector<T>;
template<typename T=int> using mat = std::vector<std::vector<T>>;
template<typename T=int> using dic = std::map<std::string, T>;
template<typename T=int> using dict = std::unordered_map<std::string, T>;
template<typename T=int> using pqg = std::priority_queue<T, std::vector<T>, std::greater<>>;
template<typename T=int, class F=std::less<>> using pq = std::priority_queue<T, std::vector<T>, F>;

using vi = std::vector<int>;
using mi = std::vector<std::vector<int>>;
using vl = std::vector<ll>;
using ml = std::vector<std::vector<ll>>;
using pii = std::pair<int, int>;
using vpi = std::vector<pii>;
using pll = std::pair<ll, ll>;
using vpl = std::vector<pll>;
using vb = std::vector<bool>;

template<typename T=int> using opt = boost::optional<T>;
//using cint = boost::multiprecision::cpp_int;
//using boost::irange;

constexpr int IINF = 0x3f3f3f3f;
constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr int INF = (int)LINF;

constexpr long long MOD = 1e9+7;

#define overload4(a,b,c,d,name,...) name
#define rep1(n) for(int i=0,end_i=(n);i<end_i;++i)
#define rep2(i,n) for(int i=0,end_##i=(n);i<end_##i;++i)
#define rep3(i,a,b) for(int i=(a),end_##i=(b);i<end_##i;++i)
#define rep4(i,a,b,c) for(int i=(a),end_##i;i<end_##i;i+=(c))
#define rep(...) overload4(__VA_ARGS__,rep4,rep3,rep2,rep1)(__VA_ARGS__)
#define rrep1(n) for(int i=(n)-1;i>=0;--i)
#define rrep2(i,n) for(int i=(n)-1;i>=0;--i)
#define rrep3(i,a,b) for(int i=(b)-1,begin_##i=(a);i>=begin_##i;--i)
#define rrep4(i,a,b,c) for(int i=((b)-(a)-1)/(c)*(c)+(a),begin_##i=(a);i>=begin_##i;i-=c)
#define rrep(...) overload4(__VA_ARGS__,rrep4,rrep3,rrep2,rrep1)(__VA_ARGS__)

#define endl '\n'

#define all(a) std::begin(a), std::end(a)

#define eb emplace_back
#define pb push_back

#define mp make_pair

template<class C> constexpr C& rev(C& a) { std::reverse(std::begin(a), std::end(a)); return a; }
template<class C> constexpr C rev(C&& a) { return std::forward<C>(rev(a)); }
template<class C> constexpr C& sort(C& a) { std::sort(std::begin(a), std::end(a)); return a; }
template<class C> constexpr C sort(C&& a) { return std::forward<C>(sort(a)); }
template<class C> constexpr bool np(C& a) { return std::next_permutation(std::begin(a), std::end(a)); }
template<class C> constexpr bool pp(C& a) { return std::prev_permutation(std::begin(a), std::end(a)); }
template<class C> constexpr auto& min(C const& a) { return *std::min_element(std::begin(a), std::end(a)); }
template<class C> constexpr auto& max(C const& a) { return *std::max_element(std::begin(a), std::end(a)); }
template<class C> constexpr auto total(C const& c) { return std::accumulate(std::next(std::begin(c)), std::end(c), *std::begin(c)); }
template<class C, class F> constexpr auto foldl(C const& c, F const& f) { return std::accumulate(std::next(std::begin(c)), std::end(c), *std::begin(c), f); }

template<typename T> constexpr auto RLE(std::vector<T> const& v) { // run length encoding
  std::vector<std::pair<T, std::size_t>> w{}; auto t=std::begin(v); std::size_t cnt=0;
  for(auto i=std::begin(v); i!=std::end(v); ++i) { if(*t == *i) ++cnt; else w.emplace_back(*t, cnt), t=i, cnt=1; }
  if(cnt > 0) w.emplace_back(*t, cnt); return w;
}

template<typename T> constexpr T gcd2(T a, T b) { while(b) { T t=b; b=a%b; a=t; } return a; }
template<typename T> constexpr T lcm2(T a, T b) { return a/gcd2(a,b)*b; }
template<typename T> constexpr T extgcd2(T a, T b, T& x, T& y) { x=1; y=0; while(b) { T t=a/b; T u=b; b=a-t*b; a=u; u=y; y=x-t*y; x=u; } return a; }

template<typename T> constexpr bool chmax(T& a, T const& b) { if (a < b) { a = b; return true; } return false; }
template<typename T> constexpr bool chmin(T& a, T const& b) { if (b < a) { a = b; return true; } return false; }

template<typename T, typename U> constexpr T ceil(T const& A, U const& B) { return (A+B-1) / B; } // ceil(A/B)

#define mv(T,...) make_v<T>(__VA_ARGS__)
template<typename T> constexpr auto make_v() { return std::vector<T>(); } template<typename T> constexpr auto make_v(std::size_t a) { return std::vector<T>(a); }
template<typename T, typename... Ts> constexpr auto make_v(std::size_t a, Ts... ts) { return std::vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); }

template<typename T> struct has_itr {
  template<typename U> static auto check(U x) -> decltype(std::begin(x), std::end(x), std::true_type()); static std::false_type check(...);
  static bool constexpr value = decltype(check(std::declval<T>()))::value;
};
template<typename T, typename V, std::enable_if_t<!has_itr<T>::value, std::nullptr_t> = nullptr> constexpr void fill_v(T& t, V const& v) { t = v; }
template<typename T, typename V, std::enable_if_t< has_itr<T>::value, std::nullptr_t> = nullptr> constexpr void fill_v(T& t, V const& v) { for(auto&& e : t) fill_v(e, v); }

template<typename T, typename U> std::istream& operator>>(std::istream& is, std::pair<T, U>& p) { is >> p.first >> p.second; return is; }
template<typename T, typename U> std::ostream& operator<<(std::ostream& os, std::pair<T, U> const& p) { os << '(' << p.first << ',' << p.second << ')'; return os; }
template<typename T> std::istream& operator>>(std::istream& is, std::vector<T>& v) { for(auto&& x : v) is >> x; return is; }
template<typename T> struct is_string : std::false_type {}; template<> struct is_string<std::string> : std::true_type {};
template<typename T, std::enable_if_t<has_itr<T>::value && !is_string<T>::value, std::nullptr_t> = nullptr> std::ostream& operator<<(std::ostream& os, T const& t)
{ for(auto i=std::begin(t);i!=std::end(t);++i) { if(i!=std::begin(t)) os<<' '; os<<*i; } return os; }

template<typename T> struct is_priority_queue : std::false_type {}; template<class T, class V, class C> struct is_priority_queue<std::priority_queue<T, V, C>> : std::true_type {};
template<typename T, std::enable_if_t< is_priority_queue<T>::value, std::nullptr_t> = nullptr> constexpr auto pop_top(T& p) { auto q = p.top(); p.pop(); return q; }

struct SetDigit {
  int n; SetDigit(int _n) : n(_n) {}
  friend std::ostream& operator<<(std::ostream& os, SetDigit sd) {
    os << std::fixed << std::setprecision(sd.n); return os;
  }
};
inline auto setd(int n) { return SetDigit(n); };
struct ZeroFill {
  int n; ZeroFill(int _n) : n(_n) {}
  friend std::ostream& operator<<(std::ostream& os, ZeroFill zf) {
    os << std::setfill('0') << std::setw(zf.n);
    return os;
  }
};
inline auto zfl(int n) { return ZeroFill(n); }
struct InitIO {
  InitIO() {
    std::cin.tie(nullptr); std::cout.tie(nullptr); std::cerr.tie(nullptr);
    std::ios_base::sync_with_stdio(false);
    std::cout << setd(15) << std::boolalpha;
    std::cerr << setd(15) << std::boolalpha;
    #ifndef LOCAL
      std::cerr.rdbuf(nullptr);
    #endif
  }
} initio;
#ifdef LOCAL
  #include <boost/type_index.hpp>
  #define trace(...) Trace(" " #__VA_ARGS__ ",", __VA_ARGS__)
  template<typename T> inline std::string type_name() { return boost::typeindex::type_id_with_cvr<T>().pretty_name(); }
  template<typename T> inline char const* Trace(char const* n, T const& t) {
    auto c = std::strchr(n+1, ','); std::cerr.write(n, c-n) << " = " << t << "; // " << type_name<T>() << "\n"; return c; }
  template<typename T, typename... Ts> inline char const* Trace(char const* n, T const& t, Ts const&... ts) {
    char const* c = Trace(n, t); return Trace(c+1, ts...); }
#else
  #define trace(...)
#endif

#define in(T,...) In<T>(__VA_ARGS__)
template<typename T> inline T& sc(T& x) { std::cin >> x; return x; }
template<typename T, typename... Ts> inline void sc(T& t, Ts&... ts) { sc(t); sc(ts...); }
template<typename T=int> inline T In() { T x; sc(x); return x; }
template<typename T, typename... Ts> inline auto In(Ts ...ts) { auto v = make_v<T>(ts...); sc(v); return v; }
inline std::string input() { std::string str; std::cin >> std::ws; std::getline(std::cin, str); return str; }
template<typename T> inline void out(T const& x) { std::cout << x; }
template<typename T, typename... Ts> inline void out(T const& t, Ts const&... ts) { out(t); out(' '); out(ts...); }
template<typename T> inline void err(T const& x) { std::cerr << x; }
template<typename T, typename... Ts> inline void err(T const& t, Ts const&... ts) { err(t); err(' '); err(ts...); }
template<typename... Ts> inline void print(Ts const&... ts) { out(ts...); out('\n'); }
template<typename... Ts> inline void debug(Ts const&... ts) { err(ts...); err('\n'); }

constexpr auto yes(bool const& c) { return c ? "yes" : "no"; }
constexpr auto Yes(bool const& c) { return c ? "Yes" : "No"; }
constexpr auto YES(bool const& c) { return c ? "YES" : "NO"; }

constexpr int lim = 1e5+7;

mat<pii> G;
vi a, b, cl;

void dfs(int k=0, int p=-1, int ac=0) {
  int cnt = 1;
  rep(i, G[k].size()) {
    int j = G[k][i].first;
    if(j == p) continue;
    if(cnt == ac) ++cnt;
    cl[G[k][i].second] = cnt;
    dfs(j, k, cnt);
    ++cnt;
  }
}

signed main() {

  let N = in();

  a = mv(int, N-1);
  b = mv(int, N-1);

  rep(i, N-1) {
    sc(a[i], b[i]);
    --a[i];
    --b[i];
  }

  G = mv(pii, N, 0);
  cl = mv(int, N-1);
  rep(i, N-1) {
    G[a[i]].eb(b[i], i);
    G[b[i]].eb(a[i], i);
  }

  dfs();

  print(max(cl));
  rep(i, N-1) print(cl[i]);

  return 0;
}


Measured memory_bytes: 7936