using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("3");
WillReturn.Add("2 1");
WillReturn.Add("3 1");
//1 2
//2 2
//1 1
}
else if (InputPattern == "Input2") {
WillReturn.Add("5");
WillReturn.Add("3 4");
WillReturn.Add("5 4");
WillReturn.Add("1 2");
WillReturn.Add("1 4");
//1 3
//3 3
//2 2
//1 2
//1 1
}
else if (InputPattern == "Input3") {
WillReturn.Add("5");
WillReturn.Add("4 5");
WillReturn.Add("3 2");
WillReturn.Add("5 2");
WillReturn.Add("3 1");
//1 1
//1 1
//1 1
//1 1
//1 1
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => long.Parse(pX)).ToArray();
}
// 隣接リスト
static Dictionary<long, List<long>> mToNodeListDict = new Dictionary<long, List<long>>();
static void Main()
{
List<string> InputList = GetInputList();
long NodeCnt = long.Parse(InputList[0]);
long[] wkArr = { };
Action<string> SplitAct = (pStr) => wkArr = GetSplitArr(pStr);
foreach (string EachStr in InputList.Skip(1)) {
SplitAct(EachStr);
long FromNode = wkArr[0];
long ToNode = wkArr[1];
if (mToNodeListDict.ContainsKey(FromNode) == false) {
mToNodeListDict[FromNode] = new List<long>();
}
if (mToNodeListDict.ContainsKey(ToNode) == false) {
mToNodeListDict[ToNode] = new List<long>();
}
mToNodeListDict[FromNode].Add(ToNode);
mToNodeListDict[ToNode].Add(FromNode);
}
List<JyoutaiDef> DFSResult = ExecDFS(1);
DFSResult.Reverse();
// 葉ノード以外のノードのset
var NonLeafNodeSet = new HashSet<long>();
// リーフ番号の最小値[ノード]
var MinLeafDict = new Dictionary<long, long>();
// リーフ番号の最大値[ノード]
var MaxLeafDict = new Dictionary<long, long>();
for (long I = 1; I <= NodeCnt; I++) {
MinLeafDict[I] = long.MaxValue;
}
for (long I = 1; I <= NodeCnt; I++) {
MaxLeafDict[I] = long.MinValue;
}
long CurrLeafNo = 1;
foreach (JyoutaiDef EachJyoutai in DFSResult) {
long ParentNode = EachJyoutai.ParentNode;
long CurrNode = EachJyoutai.Node;
NonLeafNodeSet.Add(EachJyoutai.ParentNode);
if (NonLeafNodeSet.Contains(CurrNode) == false) {
MinLeafDict[CurrNode] = CurrLeafNo;
MaxLeafDict[CurrNode] = CurrLeafNo;
CurrLeafNo++;
}
if (ParentNode != -1) {
MinLeafDict[ParentNode] = Math.Min(MinLeafDict[ParentNode], MinLeafDict[CurrNode]);
MaxLeafDict[ParentNode] = Math.Max(MaxLeafDict[ParentNode], MaxLeafDict[CurrNode]);
}
}
for (long I = 1; I <= NodeCnt; I++) {
long MinLeaf = MinLeafDict[I];
long MaxLeaf = MaxLeafDict[I];
Console.WriteLine("{0} {1}", MinLeaf, MaxLeaf);
}
}
struct JyoutaiDef
{
internal int Node;
internal int ParentNode;
}
static List<JyoutaiDef> ExecDFS(int pRootNode)
{
var WillReturn = new List<JyoutaiDef>();
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.Node = pRootNode;
WillPush.ParentNode = -1;
Stk.Push(WillPush);
var VisitedSet = new HashSet<int>();
VisitedSet.Add(pRootNode);
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
WillReturn.Add(Popped);
if (mToNodeListDict.ContainsKey(Popped.Node)) {
foreach (int EachToNode in mToNodeListDict[Popped.Node]) {
if (VisitedSet.Add(EachToNode)) {
WillPush.Node = EachToNode;
WillPush.ParentNode = Popped.Node;
Stk.Push(WillPush);
}
}
}
}
return WillReturn;
}
}