#include <bits/stdc++.h>
#define GET_REP(_1, _2, _3, NAME, ...) NAME
#define rep(...) GET_REP(__VA_ARGS__, irep, _rep)(__VA_ARGS__)
#define rep1(...) GET_REP(__VA_ARGS__, irep1, _rep1)(__VA_ARGS__)
#define _rep(i, n) irep (i, 0, n)
#define _rep1(i, n) irep1(i, 1, n)
#define irep(i, a, n) for (int i = a; i < (int)(n); ++i)
#define irep1(i, a, n) for (int i = a; i <= (int)(n); ++i)
#define rrep(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define rrep1(i, n) for (int i = (int)(n); i >= 1; --i)
#define allrep(X, x) for (auto &&X : x)
#define all(x) (x).begin(), (x).end()
#ifdef LOCAL
  #include "../../Lib/cout_container.hpp"
  #define debug(x) cerr << #x " => " << x << endl
#else
  #define debug(x) 0
#endif
using lint = long long;
constexpr int    INF  = 1 << 30;
constexpr lint   INFL = 1LL << 62;
constexpr int    MOD  = (int)1e9 + 7;
constexpr double EPS  = 1e-9;
using namespace std;
namespace { struct INIT { INIT() { cin.tie(0); ios::sync_with_stdio(false); cout << fixed << setprecision(15); } } INIT; }

using Weight = int;
struct Edge {
  int src, dst;
  Weight weight;
  Edge(void) {}
  Edge(int src, int dst) : src(src), dst(dst) {}
  Edge(int src, int dst, Weight weight) : src(src), dst(dst), weight(weight) {}
};
using Edges = vector<Edge>;
using Graph = vector<Edges>;

void edges_to_graph(Graph &graph, const Edges &edges, const int vertices, const bool directed = true) {
  graph.resize(vertices);
  for (const auto &e : edges) {
    graph[e.src].push_back(e);
    if (!directed) graph[e.dst].emplace_back(Edge(e.dst, e.src, e.weight));
  }
}

class EulerTour {
  int num = 0;
  void et_dfs(const Graph &graph, const int v, const int p, const int dep) {
    tour.push_back(v);
    in[v] = num++;
    depth_tour.push_back(dep);
    depth[v] = dep;
    for (const auto &e : graph[v]) {
      if (e.dst == p) continue;
      et_dfs(graph, e.dst, v, dep + 1);
      tour.push_back(v);
      depth_tour.push_back(dep);
      ++num;
    }
    out[v] = num - 1;
  }

  public:
  vector<int> tour, depth_tour, in, out, depth;
  EulerTour(void) {}
  EulerTour(const Graph &graph, const int v) { init(graph, v); }
  void init(const Graph &graph, const int v) {
    tour.clear();
    depth_tour.clear();
    in.assign(graph.size(), 0);
    out.assign(graph.size(), 0);
    depth.assign(graph.size(), 0);
    et_dfs(graph, v, -1, 0);
  }
};

template <typename T>
class SparseTable {
  vector<vector<int>> st;
  vector<int> lookup;
  vector<T>   origin;

  public:
  SparseTable(void) {}
  SparseTable(const vector<T> &v) { init(v); }
  void init(const vector<T> &v) {
    origin = v;
    int lg_size = 0, v_size = origin.size();
    while ((1 << lg_size) <= v_size) ++lg_size;
    st.assign((1 << lg_size), vector<int>(lg_size));
    for (int i = 0; i < v_size; ++i) st[i][0] = i;
    for (int j = 1; j < lg_size; ++j) {
      for (int i = 0; i + (1 << j) <= (1 << lg_size); ++i) {
        int l = st[i][j - 1], r = st[i + (1 << (j - 1))][j - 1];
        st[i][j] = origin[l] <= origin[r] ? l : r;
      }
    }
    lookup.assign(v_size + 1, 0);
    for (int i = 2; i <= v_size; ++i) lookup[i] = lookup[i >> 1] + 1;
  }
  T query(int l, int r) {
    int lgsize = lookup[r - l];
    return min(origin[st[l][lgsize]], origin[st[r - (1 << lgsize)][lgsize]]);
  }
  int query_index(int l, int r) {
    int lgsize = lookup[r - l];
    T   lv = origin[st[l][lgsize]], rv = origin[st[r - (1 << lgsize)][lgsize]];
    if (lv < rv) return st[l][lgsize];
    else if (lv > rv) return st[r - (1 << lgsize)][lgsize];
    else return min(st[l][lgsize], st[r - (1 << lgsize)][lgsize]);
  }
};

class LowestCommonAncestor {
  class PM1RangeMinimumQuery {
    int bl_size, bl_num;
    vector<int> origin, bl_bit;
    vector<vector<vector<int>>> lookup;
    SparseTable<pair<int, int>> st;

    public:
    PM1RangeMinimumQuery(void) {}
    void init(const vector<int> &v) {
      origin = v;
      int vsize = origin.size();
      bl_size = 0;
      while ((1 << bl_size) < vsize) ++bl_size;
      bl_size = max(1, (bl_size >> 1));
      while (vsize % bl_size) {
        origin.push_back(origin.back() + 1);
        ++vsize;
      }
      bl_num = vsize / bl_size;
      vector<pair<int, int>> block_min(bl_num, {INT_MAX, INT_MAX});
      for (int i = 0; i < vsize; ++i) block_min[i / bl_size] = min(block_min[i / bl_size], {origin[i], i});
      st.init(block_min);
      bl_bit.assign(bl_num, 0);
      for (int i = 0; i < bl_num; ++i) {
        int left = bl_size * i;
        for (int j = 0; j < bl_size - 1; ++j) {
          if (origin[left + j] < origin[left + j + 1]) bl_bit[i] |= (1 << j);
        }
      }
      int bit_all = 1 << (bl_size - 1);
      lookup.assign(bit_all, vector<vector<int>>(bl_size, vector<int>(bl_size)));
      for (int b = 0; b < bit_all; ++b) {
        for (int i = 0; i < bl_size; ++i) {
          int mini = 0, now = 0;
          for (int j = i + 1; j < bl_size; ++j) {
            b&(1 << (j - 1)) ? ++now : --now;
            if (mini > now) {
              mini = now;
              lookup[b][i][j] = j - i;
            } else {
              lookup[b][i][j] = lookup[b][i][j - 1];
            }
          }
        }
      }
    }
    int query_index(int l, int r) {
      int lb = l / bl_size, rb = r / bl_size;
      int lbpos = l % bl_size, rbpos = r % bl_size;
      if (lb == rb) return l + lookup[bl_bit[lb]][lbpos][rbpos];
      int lbmini = l, rbmini = r;
      if (lbpos != 0) {
        lbmini = l + lookup[bl_bit[lb]][lbpos][bl_size - 1];
        ++lb;
      }
      if (rbpos != bl_size - 1) {
        rbmini = r - rbpos + lookup[bl_bit[rb]][0][rbpos];
        --rb;
      }
      int mini = origin[lbmini] < origin[rbmini] ? lbmini : rbmini;
      if (lb <= rb) {
        pair<int, int> stpair = st.query(lb, rb + 1);
        if (stpair.first < origin[mini]) mini = stpair.second;
      }
      return mini;
    }
  };
  PM1RangeMinimumQuery rmq;

  public:
  EulerTour et;
  LowestCommonAncestor(void) {}
  LowestCommonAncestor(const Graph &graph, const int v) { init(graph, v); }
  void init(const Graph &graph, const int v) {
    et.init(graph, v);
    rmq.init(et.depth_tour);
  }
  int query(const int a, const int b) {
    int l = et.in[a], r = et.in[b];
    if (l > r) swap(l, r);
    int idx = rmq.query_index(l, r);
    return et.tour[idx];
  }
};

vector<vector<int>> c;
Graph graph;
int main(void) {
  int n;
  cin >> n;
  c.resize(n);
  rep (i, n) {
    int k;
    cin >> k;
    rep (j, k) {
      int x;
      cin >> x;
      c[i].push_back(x);
    }
  }
  int q;
  cin >> q;
  vector<int> u(q), v(q);
  rep (i, q) cin >> u[i] >> v[i];
  graph.resize(n);
  rep (i, n) {
    allrep (C, c[i]) {
      graph[i].emplace_back(Edge(i, C));
    }
  }
  LowestCommonAncestor lca(graph, 0);
  vector<int> ans(q);
  rep (i, q) ans[i] = lca.query(u[i], v[i]);
  rep (i, q) cout << ans[i] << "\n";
  return 0;
}


Measured memory_bytes: 26040