#include <bits/stdc++.h>
using namespace std;
const int SOURCE_GRAPH = 1;
const int TARGET_GRAPH = 2;
struct Iterstate {
  int node;
  int edge;
};
struct Pare {
  Pare() {}
  Pare(int first, int second) {
    firstNd = first;
    secondNd = second;
  }
  int firstNd;
  int secondNd;
};
struct Quadro {
  int ffrom;
  int sfrom;
  int fto;
  int sto;
};
struct ParRel {
  ParRel(int current, int parrent) {
    curNode = current;
    parNode = parrent;
  }
  int curNode;
  int parNode;
};
class Node {
 public:
  Node() {
    forwdPtr = -1;
    backwdPtr = -1;
  }
  int backwdPtr;
  int forwdPtr;
};
class Edge {
 public:
  Edge() : trgNode(-1), nextNode(-1), istrs(-1) {}
  int trgNode;
  int nextNode;
  int istrs;
};
class Graph {
 public:
  Graph(int nodeCnt, int graphType, Pare* src);
  ~Graph();
  int getTrgNode(int edge);
  bool existEdge(int srcNode, int trgNode);
  bool existBackEdge(int parNode, int trgNode);
  Pare* findCounterEdge(int srcNode, int trgNode, Graph* trgGraph);

 private:
  Node* nodes;
  Edge* edges;
  int listOfEdges;
  int nodesSize;
  int edgesSize;
  int dopSize;
  vector<int> stack;
  list<int> queue;
  int result;
  int sz;
  void init(int cnt, int startPtr, Pare* src);
  void reorderGraph();
  bool findNode(int firstNode, int targetNode, int restrNode);
  int getFreeEdge();
  int addEdgeBack(int parentNd, int curNd, int istrs);
  void deleteEdgeBack(int curNd);
  void deleteEdge(int firstNode, int secondNode);
  friend ostream& operator<<(ostream& out, const Graph& graph);
  friend class Iter;
};
Graph::Graph(int nodeCnt, int graphType, Pare* src) {
  result = -1;
  sz = nodeCnt;
  listOfEdges = 0;
  nodesSize = nodeCnt + 1;
  edgesSize = nodeCnt * 2;
  dopSize = 5;
  nodes = new Node[nodesSize];
  edges = new Edge[edgesSize + dopSize];
  init(nodeCnt, graphType == SOURCE_GRAPH ? 1 : nodeCnt, src);
}
Graph::~Graph() {
  delete[] nodes;
  delete[] edges;
}
Pare* Graph::findCounterEdge(int srcNode, int trgNode, Graph* trgGraph) {
  Pare* res = new Pare;
  int newParent = srcNode;
  int curNode = trgNode;
  int parNode = edges[nodes[curNode].backwdPtr].trgNode;
  int curNdIstrs =
      addEdgeBack(newParent, curNode,
                  trgGraph->edges[trgGraph->nodes[trgNode].backwdPtr].istrs);
  while (curNode != 1 && trgGraph->existBackEdge(curNode, parNode)) {
    newParent = curNode;
    curNode = parNode;
    if (curNode != 1) {
      parNode = edges[nodes[curNode].backwdPtr].trgNode;
      curNdIstrs = addEdgeBack(newParent, curNode, curNdIstrs);
    }
  }
  if (curNode != 1) {
    res->firstNd = curNdIstrs;
    res->secondNd = edges[nodes[trgNode].backwdPtr].istrs;
  } else {
    res->firstNd = -1;
    res->secondNd = -1;
  }
  return res;
}
int Graph::getTrgNode(int edge) { return edges[edge].trgNode; }
bool Graph::existEdge(int parNode, int trgNode) {
  int ptr = nodes[parNode].forwdPtr;
  while (ptr != -1) {
    if (edges[ptr].trgNode == trgNode) return true;
    ptr = edges[ptr].nextNode;
  }
  return false;
}
bool Graph::existBackEdge(int parNode, int trgNode) {
  int ptr = nodes[trgNode].backwdPtr;
  if (ptr != -1) {
    if (edges[ptr].trgNode == parNode) return true;
  }
  return false;
}
void Graph::init(int cnt, int startPtr, Pare* src) {
  for (int ptr = startPtr; ptr < startPtr + cnt - 1; ptr++) {
    edges[listOfEdges].trgNode = src[ptr].secondNd;
    edges[listOfEdges].nextNode = nodes[src[ptr].firstNd].forwdPtr;
    edges[listOfEdges].istrs = ptr;
    nodes[src[ptr].firstNd].forwdPtr = listOfEdges;
    listOfEdges++;
    edges[listOfEdges].trgNode = src[ptr].firstNd;
    edges[listOfEdges].nextNode = nodes[src[ptr].secondNd].forwdPtr;
    edges[listOfEdges].istrs = ptr;
    nodes[src[ptr].secondNd].forwdPtr = listOfEdges;
    listOfEdges++;
  }
  for (int i = 1; i <= dopSize; i++) {
    listOfEdges++;
    edges[listOfEdges].nextNode = listOfEdges - 1;
  }
  reorderGraph();
}
void Graph::reorderGraph() {
  ParRel* curNode = NULL;
  int curEdge;
  int curIstrs;
  list<ParRel*> queue;
  ParRel* pr = new ParRel(1, -1);
  queue.push_back(pr);
  while (!queue.empty()) {
    if (curNode != NULL) {
      delete curNode;
    }
    curNode = queue.back();
    queue.pop_back();
    curEdge = nodes[curNode->curNode].forwdPtr;
    while (curEdge != -1) {
      if (edges[curEdge].trgNode == curNode->parNode) {
        curIstrs = edges[curEdge].istrs;
        curEdge = edges[curEdge].nextNode;
        deleteEdge(curNode->curNode, curNode->parNode);
        int freeEdge = getFreeEdge();
        edges[freeEdge].trgNode = curNode->parNode;
        edges[freeEdge].nextNode = -1;
        edges[freeEdge].istrs = curIstrs;
        nodes[curNode->curNode].backwdPtr = freeEdge;
        continue;
      } else {
        queue.push_front(new ParRel(edges[curEdge].trgNode, curNode->curNode));
        curEdge = edges[curEdge].nextNode;
      }
    }
  }
  if (curNode != NULL) {
    delete curNode;
  }
  queue.clear();
}
void Graph::deleteEdge(int firstNode, int secondNode) {
  int ptr = nodes[firstNode].forwdPtr;
  int prev = -1;
  int tmp;
  while (ptr != -1) {
    if (edges[ptr].trgNode == secondNode) {
      if (prev == -1) {
        tmp = nodes[firstNode].forwdPtr;
        nodes[firstNode].forwdPtr = edges[ptr].nextNode;
        edges[tmp].nextNode = listOfEdges;
        listOfEdges = tmp;
      } else {
        edges[prev].nextNode = edges[ptr].nextNode;
        edges[ptr].nextNode = listOfEdges;
        listOfEdges = ptr;
      }
      return;
    } else {
      prev = ptr;
      ptr = edges[ptr].nextNode;
    }
  }
}
void Graph::deleteEdgeBack(int curNd) {
  edges[nodes[curNd].backwdPtr].nextNode = listOfEdges;
  listOfEdges = nodes[curNd].backwdPtr;
  nodes[curNd].backwdPtr = -1;
}
bool Graph::findNode(int firstNode, int targetNode, int restrNode) {
  int curNode;
  int curEdge;
  queue.clear();
  queue.push_back(firstNode);
  while (!queue.empty()) {
    curNode = queue.back();
    queue.pop_back();
    curEdge = nodes[curNode].forwdPtr;
    while (curEdge != -1) {
      if (edges[curEdge].trgNode == targetNode) {
        return true;
      }
      if (edges[curEdge].trgNode != restrNode)
        queue.push_front(edges[curEdge].trgNode);
      curEdge = edges[curEdge].nextNode;
    }
  }
  queue.clear();
  list<int>().swap(queue);
  return false;
}
int Graph::getFreeEdge() {
  int ret = listOfEdges;
  listOfEdges = edges[listOfEdges].nextNode;
  return ret;
}
int Graph::addEdgeBack(int parentNd, int curNd, int istrs) {
  int res = edges[nodes[curNd].backwdPtr].istrs;
  deleteEdgeBack(curNd);
  int freeEdge = getFreeEdge();
  edges[freeEdge].trgNode = parentNd;
  edges[freeEdge].nextNode = -1;
  edges[freeEdge].istrs = istrs;
  nodes[curNd].backwdPtr = freeEdge;
  return res;
}
ostream& operator<<(ostream& out, const Graph& graph) {
  for (int i = 1; i <= graph.sz; i++) {
    if (graph.nodes[i].backwdPtr != -1) {
      out << graph.edges[graph.nodes[i].backwdPtr].trgNode << '|'
          << graph.edges[graph.nodes[i].backwdPtr].istrs << "<-";
    }
    out << i;
    int ptr = graph.nodes[i].forwdPtr;
    while (ptr != -1) {
      out << "->" << graph.edges[ptr].trgNode << '|' << graph.edges[ptr].istrs;
      ptr = graph.edges[ptr].nextNode;
    }
    out << endl;
  }
  return out;
}
class Iter {
 public:
  Iter(Graph* cont) {
    graph = cont;
    it = new Iterstate;
    it->edge = -1;
    it->node = 0;
    nds.push_front(1);
  }
  ~Iter() { delete it; }
  Iterstate* next() {
    if (it->edge != -1 && graph->edges[it->edge].nextNode != -1) {
      it->edge = graph->edges[it->edge].nextNode;
      nds.push_front(graph->edges[it->edge].trgNode);
    } else {
      if (nds.size() != 0) {
        do {
          it->node = nds.back();
          nds.pop_back();
          it->edge = graph->nodes[it->node].forwdPtr;
        } while (it->edge == -1 && nds.size() != 0);
        if (it->edge != -1) nds.push_front(graph->edges[it->edge].trgNode);
        if (nds.size() == 0) it->node = -1;
      }
    }
    return it;
  }

 private:
  list<int> nds;
  Graph* graph;
  Iterstate* it;
  int curNode, curEdge;
};
vector<Pare*> getPermutations(Graph* srcGraph, Graph* trgGraph) {
  vector<Pare*> pares;
  Iter* iter = new Iter(trgGraph);
  Iterstate* it = iter->next();
  while (it->node != -1) {
    if (srcGraph->existBackEdge(it->node, trgGraph->getTrgNode(it->edge))) {
    } else {
      Pare* res = srcGraph->findCounterEdge(
          it->node, trgGraph->getTrgNode(it->edge), trgGraph);
      if (res->firstNd != -1) {
        pares.push_back(res);
      }
    }
    it = iter->next();
  }
  delete iter;
  return pares;
}
int main() {
  Pare* pares;
  string s;
  vector<string> strs;
  getline(cin, s, (char)cin.eof());
  int prev = 0;
  int cur;
  while ((cur = s.find('\n', prev)) != (int)string::npos) {
    strs.push_back(s.substr(prev, cur - prev));
    prev = cur + 1;
  }
  int sz = atoi(strs[0].c_str());
  pares = new Pare[sz * 2 - 1];
  for (int i = 1; i < sz * 2 - 1; i++) {
    int ptr = strs[i].find(' ');
    pares[i].firstNd = atoi((strs[i].substr(0, ptr)).c_str());
    pares[i].secondNd = atoi((strs[i].substr(ptr + 1)).c_str());
  }
  Graph* srcGraph = new Graph(sz, SOURCE_GRAPH, pares);
  Graph* trgGraph = new Graph(sz, TARGET_GRAPH, pares);
  vector<Pare*> perms = getPermutations(srcGraph, trgGraph);
  vector<Pare*>::iterator it;
  cout << perms.size() << endl;
  for (it = perms.begin(); it != perms.end(); it++) {
    cout << strs[(*it)->firstNd] << ' ' << strs[(*it)->secondNd] << endl;
    delete (*it);
  }
  strs.clear();
  perms.clear();
  delete[] pares;
  delete srcGraph;
  delete trgGraph;
  return 0;
}
