// Original C++ solution (slower):
#include<bits/stdc++.h>

#include <ext/pb_ds/assoc_container.hpp>

#include <ext/pb_ds/tree_policy.hpp>

using namespace __gnu_pbds;

using namespace std;

#define Khela ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

#define ll long long int

#define llu long long unsigned int

#define pf printf

#define sf scanf

#define f first

#define s second

#define pb push_back

#define mk make_pair

#define pii pair<int ,int>

#define PII pair<ll, ll>



int dx8[] = {0, 0, 1, 1, 1, -1, -1, -1};

int dy8[] = {1,-1, 1, -1, 0, 0, -1, 1};

int dx4[] = {0, 0, 1, -1};

int dy4[] = {1, -1, 0, 0};

 

typedef tree<pair<int,int>,null_type,less<pair<int,int>>,rb_tree_tag,tree_order_statistics_node_update>ordered_set;

int Set(int N,int pos){return N=N | (1<<pos);}

int reset(int N,int pos){return N= N & ~(1<<pos);}

bool check(int N,int pos){return (bool)(N & (1<<pos));}

const int Max=1e9+100;

const int sz=2e5+100;

map<pii,int> mp;

vector<pii> edge;

vector<int> ad[sz];

int clr;

void DFS(int p,int parent,int last)

{

    int i=1;

    for(auto xx: ad[p])

    {

         if(xx != parent)

         {

             if(i==last) ++i;

             while(i>=clr) ++clr;

             int tempp=p,tempq=xx;

             if(tempp>tempq) swap(tempp,tempq);

             mp[{tempp,tempq}]=i;

             DFS(xx,p,i);

             ++i;

         }

    }

}

int main()

{

    Khela

    int a,b,c,i,j,k,q,p,x,y,ct,ct1,m,l,r,x1,y1,mn,h,sum1,in,z,mid,n,mx;

    char ch;

    double d;

    string str1,str2,str;

    bool bl,bl1;

    int t,cs=1;

    while(cin>>n)

    {

        for(i=0;i<=n;i++) ad[i].clear();

        mp.clear();

        edge.clear();

        for(i=1;i<n;i++)

        {

            cin>>p>>q;

            ad[p].pb(q);

            ad[q].pb(p);

            if(p>q) swap(p,q);

            edge.pb({p,q});

        }

        clr=0;

        DFS(1,-1,-1);

        cout<<clr-1<<endl;

        if(n==1) clr=0;

        for(auto xx: edge){

            cout<<mp[{xx.f,xx.s}]<<endl;

        }



    }



}

// Optimized solution to the same problem (faster):
#include<bits/stdc++.h>

#include <ext/pb_ds/assoc_container.hpp>

#include <ext/pb_ds/tree_policy.hpp>

using namespace __gnu_pbds;

using namespace std;

#define Go_ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

#define ll long long int

#define llu long long unsigned int

#define pf printf

#define sf scanf

#define f first

#define s second

#define pb push_back

#define mk make_pair

#define pii pair<int ,int>

#define PII pair<ll ,ll>

 

int dx8[] = {0, 0, 1, 1, 1, -1, -1, -1};

int dy8[] = {1,-1, 1, -1, 0, 0, -1, 1};

int dx4[] = {0, 0, 1, -1};

int dy4[] = {1, -1, 0, 0};

 

typedef tree<pair<int,int>,null_type,less<pair<int,int>>,rb_tree_tag,tree_order_statistics_node_update>ordered_set;

int Set(int N,int pos){return N=N | (1<<pos);}

int reset(int N,int pos){return N= N & ~(1<<pos);}

bool check(int N,int pos){return (bool)(N & (1<<pos));}

const int Max=1e9+100;

const ll  MAX=1e18+500;

const int sz=1e5+100;

vector<int> ad[sz];

int deg[sz];

int mx;

vector<pii> edge;

map<pii,int> mp;

void DFS(int p,int parent,int last)

{

     int col=1;

     for(auto xx: ad[p])

     {

         if(col==last) ++col;

         if(xx != parent){

             (p>xx) ? mp[{xx,p}]=col : mp[{p,xx}]=col;

             DFS(xx,p,col);

             ++col;

         }

     }

}

int main()

{

    Go_

    int a,b,c,i,j,k,q,p,x,y,ct,ct1,m,l,r,x1,y1,mn,h,sum1,in,z,mid,n;

    char ch;

    double d;

    string str1,str2,str;

    bool bl,bl1;

    int t,cs=1;

    

    mx=0;

    cin>>n;

    for(i=1;i<n;i++){

        cin>>p>>q;

        ad[p].push_back(q),ad[q].push_back(p);

        ++deg[p],++deg[q];

        mx=max(mx,max(deg[p],deg[q]));

        if(p>q) swap(p,q);

        edge.push_back({p,q});

    }



    DFS(1,-1,0);

    cout<<mx<<endl;



    for(auto xx: edge){

        cout<<mp[{xx.f,xx.s}]<<endl;

    }



    

    



}

// Fastest known solution for this problem: