Problem:
LCA: Lowest Common Ancestor
For a rooted tree, find the lowest common ancestor of two nodes
u
and
v
.
The given tree consists of
n
nodes and every node has a unique ID from 0 to
n
-1 where 0 is the root.
Input
n
k
0
c
1
c
2
...
c
k
0
k
1
c
1
c
2
...
c
k
1
:
k
n-1
c
1
c
2
...
c
k
n-1
q
u
1
v
1
u
2
v
2
:
u
q
v
q
The first line of the input includes an integer
n
, the number of nodes of the tree.
In the next
n
lines, the information of node
i
is given.
k
i
is the number of children of node
i
, and
c
1
, ...
c
k
i
are node IDs of 1st, ...
k
th child of node
i
.
In the next line, the number of queryies
q
is given. In the next
q
lines, pairs of
u
and
v
are given as the queries.
Output
For each query, print the LCA of
u
and
v
in a line.
Constraints
1 ≤ n ≤ 100000
1 ≤ q ≤ 100000
Sample Input 1
8
3 1 2 3
2 4 5
0
0
0
2 6 7
0
0
4
4 6
4 7
4 3
5 2
Sample Output 1
1
1
0
0

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using ld = long double;
using vi = vector<int>;
using vvi = vector<vector<int>>;
using vvvi = vector<vector<vector<int>>>;
using vll = vector<ll>;
using vvll = vector<vector<ll>>;
using vvvll = vector<vector<vector<ll>>>;
using vd = vector<double>;
using vvd = vector<vector<double>>;
using vstr = vector<string>;
using vchar = vector<char>;
using vvchar = vector<vector<char>>;
using vb = vector<bool>;
using vvb = vector<vector<bool>>;
using vvvb = vector<vector<vector<bool>>>;
using pii = pair<int,int>;
using pll = pair<long long, long long>;
using Graph = vector<vector<int>>;
using WeightedGraph = vector<vector<pair<int,ll>>>;
const int dx[8] = {1, 0, -1, 0, 1, 1, -1, -1};
const int dy[8] = {0, 1, 0, -1, 1, -1, 1, -1};

#define rep(i, a, b) for(ll i = (ll)a; i < (ll)b; i++)
#define REP(i, a, b) for (ll i = (ll)a; i <= (ll)b; i++)
#define rrep(i, a, b) for(ll i = (ll)b-1; i >= (ll)a; i--)
#define RREP(i, a, b) for(ll i = (ll)b; i >= (ll)a; i--)
#define all(v) v.begin(), v.end()
#define rall(v) v.rbegin(), v.rend()
#define YESNO(flag) cout << (flag ? "Yes" : "No") << "\n"
#define spa " "
#define mint modint<MOD>

const int inf = 1070000000;
const long long INF = 4500000000000000000;
//const long long MOD = 998244353;
const long long MOD = 1000000007;
const double pi = 3.141592653589793238;
const double eps = (1e-10);
const string ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string abc = "abcdefghijklmnopqrstuvwxyz";

template<typename T> inline bool chmin(T& a, T b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template<typename T> inline bool chmax(T& a, T b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template<typename S, typename T> pair<S, T> operator+(pair<S, T> a, pair<S, T> b) {
    pair<S, T> ans;
    ans.first = a.first + b.first;
    ans.second = a.second + b.second;
    return ans;
} 
template<typename T> vector<T> operator+(vector<T> a, vector<T> b) {
    int N = max(a.size(), b.size());
    vector<T> c(N);
    for(int i = 0; i < N; i++) {
        if(i >= a.size()) c[i] = b[i];
        else if(i >= b.size()) c[i] = a[i];
        else c[i] = a[i] + b[i];
    }
    return c;
}
template<typename T> vector<T> operator*(vector<T> a, vector<T> b) {
    int N = max(a.size(), b.size()), n = min(a.size(), b.size());
    vector<T> c(N, 0);
    for(int i = 0; i < n; i++) {
        c[i] = a[i] * b[i];
    }
    return c;
}

//拡張ユークリッド
ll ext_gcd(ll a, ll b, ll &x, ll &y) {
    if(b == 0) {
        x = 1, y = 0;
        return a;
    }

    ll q = a/b;
    ll g = ext_gcd(b, a-q*b, x, y);
    ll z = x-q*y;
    x = y, y = z;
    return g;
}
//mod mにおけるaの逆元. aとmは互いに素.
ll inverse(ll a, ll m) {
    ll x, y;
    ext_gcd(a, m, x, y);
    x = (x+m) % m;
    return x;
}
//modint
template<std::uint_fast64_t mod> struct modint {
    using u64 = std::uint_fast64_t;
    u64 val;

    modint(u64 v = 0) : val(v % mod) {}

    bool operator==(modint other) {
        return val == other.val;
    }
    bool operator<(const modint& other) const {
        return val < other.val;
    }
    // operator> (大なり)
    bool operator>(const modint& other) const {
        return val > other.val;
    }
    modint operator+(modint other) {
        modint<mod> ans;
        ans.val = val + other.val;
        if(ans.val >= mod) ans.val -= mod;
        return ans;
    }
    modint operator-(modint other) {
        modint<mod> ans;
        if(val < other.val) val += mod;
        ans.val = val - other.val;
        return ans;
    }
    modint operator*(modint other) {
        modint<mod> ans;
        ans.val = (val * other.val) % mod;
        return ans;
    }
    modint operator/(modint other) {
        modint<mod> ans;
        ans.val = (val * inverse(other.val, mod)) % mod;
        return ans;
    }
    modint &operator+=(modint other) {
        *this = *this + other;
        return *this;
    }
    modint &operator-=(modint other) {
        *this = *this - other;
        return *this;
    }
    modint &operator*=(modint other) {
        *this = *this * other;
        return *this;
    }
    modint &operator/=(modint other) {
        *this = *this / other;
        return *this;
    }
    modint &operator++() {
        *this += 1;
        return *this;
    }
    modint &operator++(int) {
        ++(*this);
        return *this;
    }
    modint &operator--() {
        *this -= 1;
        return *this;
    }
    modint &operator--(int) {
        --(*this);
        return *this;
    }
};
//modintの入出力
template<std::uint_fast64_t mod> std::ostream& operator<<(std::ostream &os, const modint<mod> &x) {
    // modint の 'val' メンバを出力ストリーム(os)に渡す
    os << x.val;
    return os; // ストリームを返す (チェーンのため)
}
template<std::uint_fast64_t mod> std::istream& operator>>(std::istream& is, modint<mod> &x) {
    // modintが内部で使っているu64型を取得
    using u64 = typename modint<mod>::u64; 
    
    u64 input_value;
    is >> input_value; // (1) istream(cin) から一時変数に値を読み込む
    
    // (2) 読み込んだ値を使って modint を再構築する
    //     modintのコンストラクタが (input_value % mod) を処理してくれる
    x = modint<mod>(input_value); 
    
    return is; // (3) ストリームを返す (チェーンのため)
}
//累乗, x^n
template<typename T> T pow(T x, int n) {
    T ans = 1;
    while(n > 0) {
        if(n & 1) ans *= x;
        x *= x;
        n >>= 1; 
    }
    return ans;
}
//階乗, n!
template<typename T> T fact(T n) {
    if(n == 0) return 1;
    return n * fact(n-1);
}
//nPk
template<typename T> T perm(T n, T k) {
    if(k == 0) return 1;
    return n * perm(n-1, k-1);
}
//nCk
template<typename T> T comb(T n, T k) {
    if(k > n) return 0;
    return perm(n, min(k, n-k))/fact(min(k, n-k));
}
//ord(n)
ll ord(ll n, ll p) {
    int ans = 0;
    while(n % p == 0) {
        n /= p;
        ans++;
    }
    return ans;
}
//ルジャンドルの公式ord(n!)
ll factord(ll n, ll p) {
    ll ans = 0;
    ll q = p, tmp = n;
    while(tmp > 0) {
        tmp = n/q;
        ans += tmp;
        q *= p;
    }
    return ans;
}
//素因数分解, O(√N)
vector<pair<ll,ll>> factorize(ll N) {
    vector<pair<ll,ll>> ans;
    for(ll p = 2; p*p <= N; p++) {
        int ex = 0;
        while(N % p == 0) {
            N /= p;
            ex++;
        }
        if(ex != 0) ans.push_back({p,ex});
    }
    if(N != 1) ans.push_back({N,1});
    return ans;
}
//素数判定
bool isprime(long long x) {
    if(x == 1) return false;
    
    for(long long i = 2; i*i <= x; i++) {
        if(x % i == 0) return false;
    }
    return true;
}
//エラトステネスの篩
struct Eratosthenes {
    vector<bool> prime;
    vector<int> minfactor;
    vector<int> mobius;
    vector<ll> euler;

    //コンストラクタ O(NloglogN)
    Eratosthenes(int N) : prime(N+1, true),
                          minfactor(N+1, -1),
                          mobius(N+1, 1),
                          euler(N+1, 1) {
        prime[0] = false;
        prime[1] = false;
        minfactor[1] = 1;
        REP(i,1,N) euler[i] = i;

        for(int p = 2; p <= N; p++) {
            if(!prime[p]) continue;

            minfactor[p] = p;
            mobius[p] = -1;
            euler[p] = p-1;

            for(int q = 2; p*q <= N; q++) {
                prime[p*q] = false;
                if(minfactor[p*q] == -1) minfactor[p*q] = p;
                if(q % p == 0) mobius[p*q] = 0;
                else mobius[p*q] *= -1;
                euler[p*q] = euler[p*q]*(p-1)/p;
            }
        }
    }

    //素因数分解 O(logn)
    vector<pair<int,int>> factorize(int n) {
        vector<pair<int,int>> ans;
        while(n > 1) {
            int p = minfactor[n], exp = 0;
            while(n % p == 0) {
                n /= p;
                exp++;
            }
            ans.push_back({p, exp});
        }
        return ans;
    }
    //約数列挙 O(σ(n))
    vector<int> divisors(int n) {
        vector<int> ans = {1};
        while(n > 1) {
            int p = minfactor[n], d = 1, s = ans.size();
            while(n % p == 0) {
                d *= p;
                for(int i = 0; i < s; i++) {
                    ans.push_back(ans[i]*d);
                }
                n /= p;
            }
        }
        return ans;
    }
    //素数関数 O(n)
    int count(int n) {
        int ans = 0;
        REP(i,2,n) if(prime[i]) ans++;
        return ans;
    }
};
//高速ゼータ変換
template<typename T> void fast_zeta(vector<T> &f) {
    int N = f.size()-1;
    Eratosthenes sieve(N);
    for(int p = 2; p < N; p++) {
        if(!sieve.prime[p]) continue;
        
        for(int k = N/p; k >= 1; k--) {
            f[k] += f[k*p];
        }
    }
}
//高速メビウス変換
template<typename T> void fast_mobius(vector<T> &F) {
    int N = F.size()-1;
    Eratosthenes sieve(N);
    for(int p = 2; p < N; p++) {
        if(!sieve.prime[p]) continue;

        for(int k = 1; k*p <= N; k++) {
            F[k] -= F[k*p];
        }
    }
}
//添字GCD畳み込み
template<typename T> vector<T> gcd_conv(vector<T> &f, vector<T> &g) {
    int N = max(f.size(), g.size());
    vector<T> F(N, 0), G(N, 0), H(N);
    for(int i = 0; i < f.size(); i++) F[i] = f[i];
    for(int i = 0; i < g.size(); i++) G[i] = g[i];
    fast_zeta(F);
    fast_zeta(G);
    
    for(int i = 0; i < N; i++) H[i] = F[i]*G[i];
    fast_mobius(H);
    
    return H;
}

//行列積
template<typename T> vector<vector<T>> matrix_product(vector<vector<T>> A, vector<vector<T>> B) {
    int H = A.size(), W = A[0].size(), P = B.size(), Q = B[0].size();
    if(W != P) {
        cout << "matrix_size_error" << "\n";
        return vector<vector<T>>(0);
    }

    vector<vector<T>> C(H, vector<T>(Q, 0));
    rep(i,0,H) rep(j,0,Q) {
        rep(k,0,W) {
            C[i][j] += A[i][k]*B[k][j];
        }
    }
    return C;
}
//行列累乗
template<typename T> vector<vector<T>> matrix_pow(vector<vector<T>> A, ll n) {
    int N = A.size();
    vector<vector<T>> ans(N, vector<T>(N, 0));
    rep(i,0,N) ans[i][i] = 1;
    
    while(n > 0) {
        if(n & 1) ans = matrix_product(ans, A);
        A = matrix_product(A, A);
        n >>= 1; 
    }
    return ans;
}

//1+2+...+n = n(n+1)/2
ll tri(ll n) {
    ll x = n*(n+1);
    return x/2;
}

//座標圧縮
vector<int> compress(vector<int> A) {
    vector<int> B = A;

    sort(B.begin(), B.end());
    B.erase(unique(B.begin(), B.end()), B.end());

    vector<int> res(A.size());
    for(int i = 0; i < (int)A.size(); i++) {
        res[i] = lower_bound(B.begin(), B.end(), A[i]) - B.begin();
    }
    return res;
}

//正方形グリッドの右回転, 左回転
void rotate_right(vector<vector<char>> &S) {
    int N = S.size();
    auto T = S;
    rep(i,0,N) rep(j,0,N) {
        T[i][j] = S[N-1-j][i];
    }
    S = T;
}
void rotate_left(vector<vector<char>> &S) {
    int N = S.size();
    auto T = S;
    rep(i,0,N) rep(j,0,N) {
        T[i][j] = S[j][N-1-i];
    }
    S = T;
}

//2点(a,b)と(x,y)の距離
long double dist(pair<long double, long double> a, pair<long double, long double> b) {
    return sqrt((a.first-b.first)*(a.first-b.first) + (a.second-b.second)*(a.second-b.second));
}
long double man(pair<long double, long double> a, pair<long double, long double> b) {
    return abs(a.first-b.first) + abs(a.second-b.second);
}
//内分点
pair<long double, long double> internal_division(pair<long double, long double> a, pair<long double, long double> b, long double p) {
    long double x = a.first + (b.first - a.first) * p;
    long double y = a.second + (b.second - a.second) * p;
    return {x, y};
}

//桁数
int digit(ll N) {
    if(N <= 9) return 1;

    return 1 + digit(N / 10);
}
//A進数文字列NをB進数に変換. A,Bは10以下
string base_change(string N, ll A, ll B) {
    ll X = stoll(N), Y = 0;

    ll i = 1;
    while(X != 0) {
        Y += (X % 10) * i;
        i *= A;
        X /= 10;
    }

    X = Y, Y = 0;
    i = 1;
    while(X != 0) {
        Y += (X % B) * i;
        i *= 10;
        X /= B;
    }

    return to_string(Y);
}
//回文判定
bool palin(string S) {
    rep(i,0,S.size()) {
        if(S[i] != S[S.size()-1-i]) return false;
    }
    return true;
}

//hh:mm:ss to second
int convert_to_time(string S) {
    ll h = 10*(S[0]-'0') + (S[1]-'0');
    ll m = 10*(S[3]-'0') + (S[4]-'0');
    ll s = 10*(S[6]-'0') + (S[7]-'0');
    
    return h*3600+m*60+s;
}

//DFS
void dfs(const Graph &G, int v, vector<bool> &seen) { 
    seen[v] = true;
    
    for (auto next_v : G[v]) { 
        if (!seen[next_v]) {
            dfs(G, next_v, seen);
        }
    }
}
void griddfs(const vector<vector<char>> &G, int x, int y, vector<vector<bool>> &seen) {
    int H = G.size(), W = G[0].size();
    seen[x][y] = true;
    
    for (int i = 0; i < 8; i++) { 
        int nx = x + dx[i], ny = y + dy[i];
        if(nx < 0 or nx >= H or ny < 0 or ny >= W) continue;
        if (!seen[nx][ny] and G[nx][ny] != '#') griddfs(G, nx, ny, seen);
    }
}
//BFS
void bfs(const Graph &G, int v, vector<int> &dist) {
    rep(i,0,dist.size()) dist[i] = inf;
    dist[v] = 0;
    queue<int> q;
    q.push(v);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(auto next_u : G[u]) {
            if(dist[next_u] == inf) {
                dist[next_u] = dist[u] + 1;
                q.push(next_u);
            }
        }
    }
}
void gridbfs(const vector<vector<char>> &G, int x, int y, vector<vector<int>> &dist) {
    int H = G.size(), W = G[0].size();
    dist[x][y] = 0;
    queue<pair<int, int>> q;
    q.push(make_pair(x, y));
    while(!q.empty()) {
        int x = q.front().first, y = q.front().second;
        q.pop();
        for(int i = 0; i < 4; i++) {
            int nx = x + dx[i], ny = y + dy[i];
            if(nx < 0 or nx >= H or ny < 0 or ny >= W) continue;
            if(dist[nx][ny] == inf and G[nx][ny] != '#') {
                dist[nx][ny] = dist[x][y] + 1;
                q.push(make_pair(nx, ny));
            }
        }
    }
}
//ワーシャル・フロイド
vector<vector<ll>> floyd(vector<vector<ll>> G) {
    int N = G.size();
    auto dp = G;
    for (int k = 0; k < N; k++){
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if(dp[i][j] > dp[i][k] + dp[k][j] and dp[i][k] != INF and dp[k][j] != INF) {
                    dp[i][j] = dp[i][k] + dp[k][j];
                }
            }
        }
    }
    return dp;
}
//サイクルがあるか判定
bool cycle(const Graph &G, int v, vector<bool> &seen,  vector<bool> &finished) {
    seen[v] = true;    // 行きがけ時に true になる
    for (auto v2 : G[v]) {
        //if (v2 が逆戻りの場合) continue;  // 無向グラフの場合は必要

        // 頂点 v2 がすでに探索済みの場合はスキップ 
        if (finished[v2]) continue;

        // サイクル検出
        if (seen[v2] && !finished[v2]) return true;

        // 頂点 v2 を再帰的に探索
        if (cycle(G, v2, seen, finished)) return true;
    }
    finished[v] = true;  // 帰りがけ時に true になる
    return false;
}

//Union-Find
struct UnionFind {
    vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2
    vector<ll> cnt;
    
    UnionFind(int N) : par(N),
                       cnt(N, 1) { //最初は全てが根であるとして初期化
        for(int i = 0; i < N; i++) par[i] = i;
    }
    
    int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }
    
    void unite(int x, int y) { // xとyの木を併合
        int rx = root(x); //xの根をrx
        int ry = root(y); //yの根をry
        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま
        par[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
        cnt[ry] += cnt[rx];
    }
    
    bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
        int rx = root(x);
        int ry = root(y);
        return rx == ry;
    }

    ll num(int x) { //データxと同じ木に属するデータの数を得る
        return cnt[x] = cnt[root(x)];
    }
};
//最小全域木
ll Kruskal(const WeightedGraph &G) {
    int N = G.size();
    return 0;
}
//BIT
struct BIT {
    vector<long long> array;
    const int n;

    BIT(int _n) : array(_n, 0), n(_n) {}

    //a_0 + ... + a_i
    long long sum(int i) {
        long long s = 0;
        while(i >= 0) {
            s += array[i];
            i -= (i+1) & -(i+1);
        }
        return s;
    }
    //a_i + ... + a_j
    long long sum(int i, int j) {
        long long ans_i = sum(i-1);
        long long ans_j = sum(j);
        return ans_j - ans_i;
    }
    //a_i += x
    void add(int i, long long x) {
        while(i < n) {
            array[i] += x;
            i += (i+1) & -(i+1);
        }
    }
};
//セグ木
struct SegTree {
    int n, sz;
    vector<long long> node;
    function<ll(ll, ll)> op = [](ll a, ll b) {return max(a,b);}; //セグ木上の積
    ll e = 0; //セグ木上の積の単位元

    SegTree(int n_) {
        n = 1;
        while(n < n_) n *= 2;
        node.resize(2*n-1, e);
    }
    SegTree(vector<long long> a) {
        sz = a.size();

        n = 1;
        while(n < sz) n *= 2;
        node.resize(2*n-1, e);
        for(int i = 0; i < sz; i++) node[n-1+i] = a[i];
        for(int i = n-2; i >= 0; i--) node[i] = op(node[2*i+1], node[2*i+2]);
    }

    //a_iを取得
    long long get(int i) {
        return node[n-1+i];
    }
    //[a,b)における積
    long long prod(int a, int b, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;
        if(r <= a or b <= l) return e;
        if(a <= l and r <= b) return node[k];
        long long vl = prod(a, b, 2*k+1, l, (l+r)/2);
        long long vr = prod(a, b, 2*k+2, (l+r)/2, r);
        return op(vl, vr);
    }
    void update(int i, long long x) {
        i = n-1+i;
        node[i] = x;
        while(i > 0) {
            i = (i-1)/2;
            node[i] = op(node[2*i+1], node[2*i+2]);
        }
    }
    //[a,b)でa_i >= xなる最大のiを求める
    int maxindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        if(node[k] < x or r <= a or b <= l) return a-1;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vr = maxindex(a, b, x, 2*k+2, (l+r)/2, r);
            if(vr != a-1) return vr;
            else return maxindex(a, b, x, 2*k+1, l, (l+r)/2);
        }
    }
    //[a,b)でa_i >= xなる最小のiを求める
    int minindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        if(node[k] < x or r <= a or b <= l) return b;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vl = minindex(a, b, x, 2*k+1, l, (l+r)/2);
            if(vl != b) return vl;
            else return minindex(a, b, x, 2*k+2, (l+r)/2, r);
        }
    }
};
//遅延セグ木
struct LazySegTree {
    int n;
    vector<ll> node, lazy;
    const ll ex = 0, //セグ木上の積の単位元
             em = 0; //遅延評価木上の積の単位元
    function<ll(ll, ll)> fx = [](ll x, ll y) {return x+y;}, //セグ木上の積
                         fa = [](ll x, ll a) {return x+a;}, //遅延評価木からセグ木への作用
                         fm = [](ll a, ll b) {return a+b;}, //遅延評価木上の積
                         fp = [](ll a, ll n) {return a*n;}; //区間長の作用への影響

    LazySegTree(int n_) {
        n = 1;
        while(n < n_) n *= 2;
        node.resize(2*n-1, ex);
        lazy.resize(2*n-1, em);
    }
    LazySegTree(vector<ll> a) {
        int sz = a.size();
        
        n = 1;
        while(n < sz) n *= 2;
        node.resize(2*n-1, ex);
        lazy.resize(2*n-1, em);

        for(int i = 0; i < sz; i++) node[n-1+i] = a[i];
        for(int i = n-2; i >= 0; i--) node[i] = fx(node[2*i+1], node[2*i+2]);
    }

    void eval(int k, int len) {
        if(lazy[k] == em) return;

        node[k] = fa(node[k], fp(lazy[k], len));
        if(k < n-1) {
            lazy[2*k+1] = fm(lazy[2*k+1], lazy[k]);
            lazy[2*k+2] = fm(lazy[2*k+2], lazy[k]);
        }
        lazy[k] = em;
    }
    //区間[a,b)を値xに更新
    void update(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(r <= a or b <= l) return;
        if(a <= l and r <= b) {
            lazy[k] = fm(lazy[k], x);
            eval(k, r - l);
        }else {
            update(a, b, x, 2*k+1, l, (l+r)/2);
            update(a, b, x, 2*k+2, (l+r)/2, r);
            node[k] = fx(node[2*k+1], node[2*k+2]);
        }
    }
    //区間[a,b)の積を取得
    ll prod(int a, int b, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(r <= a or b <= l) return ex;
        if(a <= l and r <= b) return node[k];
        
        ll vl = prod(a, b, 2*k+1, l, (l+r)/2);
        ll vr = prod(a, b, 2*k+2, (l+r)/2, r);
        return fx(vl, vr);
    }
    //[a,b)でa_i >= xなる最大のiを求める
    int maxindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(node[k] < x or r <= a or b <= l) return a-1;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vr = maxindex(a, b, x, 2*k+2, (l+r)/2, r);
            if(vr != a-1) return vr;
            else return maxindex(a, b, x, 2*k+1, l, (l+r)/2);
        }
    }
    //[a,b)でa_i >= xなる最小のiを求める
    int minindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(node[k] < x or r <= a or b <= l) return b;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vl = minindex(a, b, x, 2*k+1, l, (l+r)/2);
            if(vl != b) return vl;
            else return minindex(a, b, x, 2*k+2, (l+r)/2, r);
        }
    }
};
//平方分割
struct Backet {
    vector<ll> array;
    vector<ll> backet;
    int N, M;

    Backet(vector<ll> a) {
        array = a;
        N = a.size(), M = ceil(sqrt(N));
        
        backet.resize((N+M-1)/M);
        for(int i = 0; i < N; i++) {
            backet[i/M] += a[i];
        }
    }
    void add(int i, ll x) {
        array[i] += x;
        backet[i/M] += x;
    }
    //区間[i,j)の和
    ll getsum(int i, int j) {
        int l = i/M + 1, r = j/M;
        ll s = 0;
        if(l > r) {
            rep(k,i,j) s += array[k];
        }else {
            rep(k,i,l*M) s += array[k];
            rep(k,l,r) s += backet[k];
            rep(k,r*M,j) s += array[k];
        }
        return s;
    }
};

struct LCA {
    Graph G;
    int N, LOG_N;
    vector<int> depth;
    vector<vector<int>> par;

    function<void(int)> dfs = [&](int v) {
        for(auto next_v : G[v]) {
            if(depth[next_v] != inf) continue;
            depth[next_v] = depth[v] + 1;
            par[0][next_v] = v;
            dfs(next_v);
        }
        return;
    };

    LCA(Graph &g) {
        G = g;
        N = G.size();
        depth.resize(N, inf);
        depth[0] = 0;
        LOG_N = 0;
        while((1<<LOG_N) < N) LOG_N++;
        par.resize(LOG_N+1, vector<int>(N));
        dfs(0);
        rep(i,1,LOG_N) {
            rep(j,0,N) {
                if(par[i-1][j] == -1) par[i][j] = -1;
                else par[i][j] = par[i-1][par[i-1][j]];
            }
        }
    }

    int ancestor(int u, int k) {
        int g = 0;
        while(k > 0) {
            if(k % 2 == 1) u = par[g][u];
            k /= 2;
            g++;
        }
        return u;
    }

    int lca(int u, int v) {
        int d1 = depth[u], d2 = depth[v];
        if(d1 < d2) v = ancestor(v, d2-d1);
        if(d1 > d2) u = ancestor(u, d1-d2);

        if(u == v) return u;
        for(int k = LOG_N; k >= 0; k--) {
            if(par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }

    int dist(int u, int v) {
        return depth[u] + depth[v] - 2*depth[lca(u, v)];
    }
};

void solve() {
}

int main() {
    int N; cin >> N;
    Graph G(N);
    rep(i,0,N) {
        int k; cin >> k;
        while(k--) {
            int c; cin >> c;
            G[i].push_back(c);
            G[c].push_back(i);
        }
    }

    LCA tree(G);
    int Q; cin >> Q;
    while(Q--) {
        int u, v; cin >> u >> v;
        cout << tree.lca(u, v) << "\n";
    }

    return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 27584
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using ld = long double;
using vi = vector<int>;
using vvi = vector<vector<int>>;
using vvvi = vector<vector<vector<int>>>;
using vll = vector<ll>;
using vvll = vector<vector<ll>>;
using vvvll = vector<vector<vector<ll>>>;
using vd = vector<double>;
using vvd = vector<vector<double>>;
using vstr = vector<string>;
using vchar = vector<char>;
using vvchar = vector<vector<char>>;
using vb = vector<bool>;
using vvb = vector<vector<bool>>;
using vvvb = vector<vector<vector<bool>>>;
using pii = pair<int,int>;
using pll = pair<long long, long long>;
using Graph = vector<vector<int>>;
using WeightedGraph = vector<vector<pair<int,ll>>>;
const int dx[8] = {1, 0, -1, 0, 1, 1, -1, -1};
const int dy[8] = {0, 1, 0, -1, 1, -1, 1, -1};

#define rep(i, a, b) for(ll i = (ll)a; i < (ll)b; i++)
#define REP(i, a, b) for (ll i = (ll)a; i <= (ll)b; i++)
#define rrep(i, a, b) for(ll i = (ll)b-1; i >= (ll)a; i--)
#define RREP(i, a, b) for(ll i = (ll)b; i >= (ll)a; i--)
#define all(v) v.begin(), v.end()
#define rall(v) v.rbegin(), v.rend()
#define YESNO(flag) cout << (flag ? "Yes" : "No") << "\n"
#define spa " "
#define mint modint<MOD>

const int inf = 1070000000;
const long long INF = 4500000000000000000;
//const long long MOD = 998244353;
const long long MOD = 1000000007;
const double pi = 3.141592653589793238;
const double eps = (1e-10);
const string ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string abc = "abcdefghijklmnopqrstuvwxyz";

template<typename T> inline bool chmin(T& a, T b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template<typename T> inline bool chmax(T& a, T b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template<typename S, typename T> pair<S, T> operator+(pair<S, T> a, pair<S, T> b) {
    pair<S, T> ans;
    ans.first = a.first + b.first;
    ans.second = a.second + b.second;
    return ans;
} 
template<typename T> vector<T> operator+(vector<T> a, vector<T> b) {
    int N = max(a.size(), b.size());
    vector<T> c(N);
    for(int i = 0; i < N; i++) {
        if(i >= a.size()) c[i] = b[i];
        else if(i >= b.size()) c[i] = a[i];
        else c[i] = a[i] + b[i];
    }
    return c;
}
template<typename T> vector<T> operator*(vector<T> a, vector<T> b) {
    int N = max(a.size(), b.size()), n = min(a.size(), b.size());
    vector<T> c(N, 0);
    for(int i = 0; i < n; i++) {
        c[i] = a[i] * b[i];
    }
    return c;
}

//拡張ユークリッド
ll ext_gcd(ll a, ll b, ll &x, ll &y) {
    if(b == 0) {
        x = 1, y = 0;
        return a;
    }

    ll q = a/b;
    ll g = ext_gcd(b, a-q*b, x, y);
    ll z = x-q*y;
    x = y, y = z;
    return g;
}
//mod mにおけるaの逆元. aとmは互いに素.
ll inverse(ll a, ll m) {
    ll x, y;
    ext_gcd(a, m, x, y);
    x = (x+m) % m;
    return x;
}
//modint
template<std::uint_fast64_t mod> struct modint {
    using u64 = std::uint_fast64_t;
    u64 val;

    modint(u64 v = 0) : val(v % mod) {}

    bool operator==(modint other) {
        return val == other.val;
    }
    bool operator<(const modint& other) const {
        return val < other.val;
    }
    // operator> (大なり)
    bool operator>(const modint& other) const {
        return val > other.val;
    }
    modint operator+(modint other) {
        modint<mod> ans;
        ans.val = val + other.val;
        if(ans.val >= mod) ans.val -= mod;
        return ans;
    }
    modint operator-(modint other) {
        modint<mod> ans;
        if(val < other.val) val += mod;
        ans.val = val - other.val;
        return ans;
    }
    modint operator*(modint other) {
        modint<mod> ans;
        ans.val = (val * other.val) % mod;
        return ans;
    }
    modint operator/(modint other) {
        modint<mod> ans;
        ans.val = (val * inverse(other.val, mod)) % mod;
        return ans;
    }
    modint &operator+=(modint other) {
        *this = *this + other;
        return *this;
    }
    modint &operator-=(modint other) {
        *this = *this - other;
        return *this;
    }
    modint &operator*=(modint other) {
        *this = *this * other;
        return *this;
    }
    modint &operator/=(modint other) {
        *this = *this / other;
        return *this;
    }
    modint &operator++() {
        *this += 1;
        return *this;
    }
    modint &operator++(int) {
        ++(*this);
        return *this;
    }
    modint &operator--() {
        *this -= 1;
        return *this;
    }
    modint &operator--(int) {
        --(*this);
        return *this;
    }
};
//modintの入出力
template<std::uint_fast64_t mod> std::ostream& operator<<(std::ostream &os, const modint<mod> &x) {
    // modint の 'val' メンバを出力ストリーム(os)に渡す
    os << x.val;
    return os; // ストリームを返す (チェーンのため)
}
template<std::uint_fast64_t mod> std::istream& operator>>(std::istream& is, modint<mod> &x) {
    // modintが内部で使っているu64型を取得
    using u64 = typename modint<mod>::u64; 
    
    u64 input_value;
    is >> input_value; // (1) istream(cin) から一時変数に値を読み込む
    
    // (2) 読み込んだ値を使って modint を再構築する
    //     modintのコンストラクタが (input_value % mod) を処理してくれる
    x = modint<mod>(input_value); 
    
    return is; // (3) ストリームを返す (チェーンのため)
}
//累乗, x^n
template<typename T> T pow(T x, int n) {
    T ans = 1;
    while(n > 0) {
        if(n & 1) ans *= x;
        x *= x;
        n >>= 1; 
    }
    return ans;
}
//階乗, n!
template<typename T> T fact(T n) {
    if(n == 0) return 1;
    return n * fact(n-1);
}
//nPk
template<typename T> T perm(T n, T k) {
    if(k == 0) return 1;
    return n * perm(n-1, k-1);
}
//nCk
template<typename T> T comb(T n, T k) {
    if(k > n) return 0;
    return perm(n, min(k, n-k))/fact(min(k, n-k));
}
//ord(n)
ll ord(ll n, ll p) {
    int ans = 0;
    while(n % p == 0) {
        n /= p;
        ans++;
    }
    return ans;
}
//ルジャンドルの公式ord(n!)
ll factord(ll n, ll p) {
    ll ans = 0;
    ll q = p, tmp = n;
    while(tmp > 0) {
        tmp = n/q;
        ans += tmp;
        q *= p;
    }
    return ans;
}
//素因数分解, O(√N)
vector<pair<ll,ll>> factorize(ll N) {
    vector<pair<ll,ll>> ans;
    for(ll p = 2; p*p <= N; p++) {
        int ex = 0;
        while(N % p == 0) {
            N /= p;
            ex++;
        }
        if(ex != 0) ans.push_back({p,ex});
    }
    if(N != 1) ans.push_back({N,1});
    return ans;
}
//素数判定
bool isprime(long long x) {
    if(x == 1) return false;
    
    for(long long i = 2; i*i <= x; i++) {
        if(x % i == 0) return false;
    }
    return true;
}
//エラトステネスの篩
struct Eratosthenes {
    vector<bool> prime;
    vector<int> minfactor;
    vector<int> mobius;
    vector<ll> euler;

    //コンストラクタ O(NloglogN)
    Eratosthenes(int N) : prime(N+1, true),
                          minfactor(N+1, -1),
                          mobius(N+1, 1),
                          euler(N+1, 1) {
        prime[0] = false;
        prime[1] = false;
        minfactor[1] = 1;
        REP(i,1,N) euler[i] = i;

        for(int p = 2; p <= N; p++) {
            if(!prime[p]) continue;

            minfactor[p] = p;
            mobius[p] = -1;
            euler[p] = p-1;

            for(int q = 2; p*q <= N; q++) {
                prime[p*q] = false;
                if(minfactor[p*q] == -1) minfactor[p*q] = p;
                if(q % p == 0) mobius[p*q] = 0;
                else mobius[p*q] *= -1;
                euler[p*q] = euler[p*q]*(p-1)/p;
            }
        }
    }

    //素因数分解 O(logn)
    vector<pair<int,int>> factorize(int n) {
        vector<pair<int,int>> ans;
        while(n > 1) {
            int p = minfactor[n], exp = 0;
            while(n % p == 0) {
                n /= p;
                exp++;
            }
            ans.push_back({p, exp});
        }
        return ans;
    }
    //約数列挙 O(σ(n))
    vector<int> divisors(int n) {
        vector<int> ans = {1};
        while(n > 1) {
            int p = minfactor[n], d = 1, s = ans.size();
            while(n % p == 0) {
                d *= p;
                for(int i = 0; i < s; i++) {
                    ans.push_back(ans[i]*d);
                }
                n /= p;
            }
        }
        return ans;
    }
    //素数関数 O(n)
    int count(int n) {
        int ans = 0;
        REP(i,2,n) if(prime[i]) ans++;
        return ans;
    }
};
//高速ゼータ変換
template<typename T> void fast_zeta(vector<T> &f) {
    int N = f.size()-1;
    Eratosthenes sieve(N);
    for(int p = 2; p < N; p++) {
        if(!sieve.prime[p]) continue;
        
        for(int k = N/p; k >= 1; k--) {
            f[k] += f[k*p];
        }
    }
}
//高速メビウス変換
template<typename T> void fast_mobius(vector<T> &F) {
    int N = F.size()-1;
    Eratosthenes sieve(N);
    for(int p = 2; p < N; p++) {
        if(!sieve.prime[p]) continue;

        for(int k = 1; k*p <= N; k++) {
            F[k] -= F[k*p];
        }
    }
}
//添字GCD畳み込み
template<typename T> vector<T> gcd_conv(vector<T> &f, vector<T> &g) {
    int N = max(f.size(), g.size());
    vector<T> F(N, 0), G(N, 0), H(N);
    for(int i = 0; i < f.size(); i++) F[i] = f[i];
    for(int i = 0; i < g.size(); i++) G[i] = g[i];
    fast_zeta(F);
    fast_zeta(G);
    
    for(int i = 0; i < N; i++) H[i] = F[i]*G[i];
    fast_mobius(H);
    
    return H;
}

//行列積
template<typename T> vector<vector<T>> matrix_product(vector<vector<T>> A, vector<vector<T>> B) {
    int H = A.size(), W = A[0].size(), P = B.size(), Q = B[0].size();
    if(W != P) {
        cout << "matrix_size_error" << "\n";
        return vector<vector<T>>(0);
    }

    vector<vector<T>> C(H, vector<T>(Q, 0));
    rep(i,0,H) rep(j,0,Q) {
        rep(k,0,W) {
            C[i][j] += A[i][k]*B[k][j];
        }
    }
    return C;
}
//行列累乗
template<typename T> vector<vector<T>> matrix_pow(vector<vector<T>> A, ll n) {
    int N = A.size();
    vector<vector<T>> ans(N, vector<T>(N, 0));
    rep(i,0,N) ans[i][i] = 1;
    
    while(n > 0) {
        if(n & 1) ans = matrix_product(ans, A);
        A = matrix_product(A, A);
        n >>= 1; 
    }
    return ans;
}

//1+2+...+n = n(n+1)/2
ll tri(ll n) {
    ll x = n*(n+1);
    return x/2;
}

//座標圧縮
vector<int> compress(vector<int> A) {
    vector<int> B = A;

    sort(B.begin(), B.end());
    B.erase(unique(B.begin(), B.end()), B.end());

    vector<int> res(A.size());
    for(int i = 0; i < (int)A.size(); i++) {
        res[i] = lower_bound(B.begin(), B.end(), A[i]) - B.begin();
    }
    return res;
}

//正方形グリッドの右回転, 左回転
void rotate_right(vector<vector<char>> &S) {
    int N = S.size();
    auto T = S;
    rep(i,0,N) rep(j,0,N) {
        T[i][j] = S[N-1-j][i];
    }
    S = T;
}
void rotate_left(vector<vector<char>> &S) {
    int N = S.size();
    auto T = S;
    rep(i,0,N) rep(j,0,N) {
        T[i][j] = S[j][N-1-i];
    }
    S = T;
}

//2点(a,b)と(x,y)の距離
long double dist(pair<long double, long double> a, pair<long double, long double> b) {
    return sqrt((a.first-b.first)*(a.first-b.first) + (a.second-b.second)*(a.second-b.second));
}
long double man(pair<long double, long double> a, pair<long double, long double> b) {
    return abs(a.first-b.first) + abs(a.second-b.second);
}
//内分点
pair<long double, long double> internal_division(pair<long double, long double> a, pair<long double, long double> b, long double p) {
    long double x = a.first + (b.first - a.first) * p;
    long double y = a.second + (b.second - a.second) * p;
    return {x, y};
}

//桁数
int digit(ll N) {
    if(N <= 9) return 1;

    return 1 + digit(N / 10);
}
//A進数文字列NをB進数に変換. A,Bは10以下
string base_change(string N, ll A, ll B) {
    ll X = stoll(N), Y = 0;

    ll i = 1;
    while(X != 0) {
        Y += (X % 10) * i;
        i *= A;
        X /= 10;
    }

    X = Y, Y = 0;
    i = 1;
    while(X != 0) {
        Y += (X % B) * i;
        i *= 10;
        X /= B;
    }

    return to_string(Y);
}
//回文判定
bool palin(string S) {
    rep(i,0,S.size()) {
        if(S[i] != S[S.size()-1-i]) return false;
    }
    return true;
}

//hh:mm:ss to second
int convert_to_time(string S) {
    ll h = 10*(S[0]-'0') + (S[1]-'0');
    ll m = 10*(S[3]-'0') + (S[4]-'0');
    ll s = 10*(S[6]-'0') + (S[7]-'0');
    
    return h*3600+m*60+s;
}

//DFS
void dfs(const Graph &G, int v, vector<bool> &seen) { 
    seen[v] = true;
    
    for (auto next_v : G[v]) { 
        if (!seen[next_v]) {
            dfs(G, next_v, seen);
        }
    }
}
void griddfs(const vector<vector<char>> &G, int x, int y, vector<vector<bool>> &seen) {
    int H = G.size(), W = G[0].size();
    seen[x][y] = true;
    
    for (int i = 0; i < 8; i++) { 
        int nx = x + dx[i], ny = y + dy[i];
        if(nx < 0 or nx >= H or ny < 0 or ny >= W) continue;
        if (!seen[nx][ny] and G[nx][ny] != '#') griddfs(G, nx, ny, seen);
    }
}
//BFS
void bfs(const Graph &G, int v, vector<int> &dist) {
    rep(i,0,dist.size()) dist[i] = inf;
    dist[v] = 0;
    queue<int> q;
    q.push(v);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(auto next_u : G[u]) {
            if(dist[next_u] == inf) {
                dist[next_u] = dist[u] + 1;
                q.push(next_u);
            }
        }
    }
}
void gridbfs(const vector<vector<char>> &G, int x, int y, vector<vector<int>> &dist) {
    int H = G.size(), W = G[0].size();
    dist[x][y] = 0;
    queue<pair<int, int>> q;
    q.push(make_pair(x, y));
    while(!q.empty()) {
        int x = q.front().first, y = q.front().second;
        q.pop();
        for(int i = 0; i < 4; i++) {
            int nx = x + dx[i], ny = y + dy[i];
            if(nx < 0 or nx >= H or ny < 0 or ny >= W) continue;
            if(dist[nx][ny] == inf and G[nx][ny] != '#') {
                dist[nx][ny] = dist[x][y] + 1;
                q.push(make_pair(nx, ny));
            }
        }
    }
}
//ワーシャル・フロイド
vector<vector<ll>> floyd(vector<vector<ll>> G) {
    int N = G.size();
    auto dp = G;
    for (int k = 0; k < N; k++){
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if(dp[i][j] > dp[i][k] + dp[k][j] and dp[i][k] != INF and dp[k][j] != INF) {
                    dp[i][j] = dp[i][k] + dp[k][j];
                }
            }
        }
    }
    return dp;
}
//サイクルがあるか判定
bool cycle(const Graph &G, int v, vector<bool> &seen,  vector<bool> &finished) {
    seen[v] = true;    // 行きがけ時に true になる
    for (auto v2 : G[v]) {
        //if (v2 が逆戻りの場合) continue;  // 無向グラフの場合は必要

        // 頂点 v2 がすでに探索済みの場合はスキップ 
        if (finished[v2]) continue;

        // サイクル検出
        if (seen[v2] && !finished[v2]) return true;

        // 頂点 v2 を再帰的に探索
        if (cycle(G, v2, seen, finished)) return true;
    }
    finished[v] = true;  // 帰りがけ時に true になる
    return false;
}

//Union-Find
struct UnionFind {
    vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2
    vector<ll> cnt;
    
    UnionFind(int N) : par(N),
                       cnt(N, 1) { //最初は全てが根であるとして初期化
        for(int i = 0; i < N; i++) par[i] = i;
    }
    
    int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }
    
    void unite(int x, int y) { // xとyの木を併合
        int rx = root(x); //xの根をrx
        int ry = root(y); //yの根をry
        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま
        par[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
        cnt[ry] += cnt[rx];
    }
    
    bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
        int rx = root(x);
        int ry = root(y);
        return rx == ry;
    }

    ll num(int x) { //データxと同じ木に属するデータの数を得る
        return cnt[x] = cnt[root(x)];
    }
};

//最小全域木
ll Kruskal(const WeightedGraph &G) {
    return 0;
}

//BIT
struct BIT {
    vector<long long> array;
    const int n;

    BIT(int _n) : array(_n, 0), n(_n) {}

    //a_0 + ... + a_i
    long long sum(int i) {
        long long s = 0;
        while(i >= 0) {
            s += array[i];
            i -= (i+1) & -(i+1);
        }
        return s;
    }
    //a_i + ... + a_j
    long long sum(int i, int j) {
        long long ans_i = sum(i-1);
        long long ans_j = sum(j);
        return ans_j - ans_i;
    }
    //a_i += x
    void add(int i, long long x) {
        while(i < n) {
            array[i] += x;
            i += (i+1) & -(i+1);
        }
    }
};
//セグ木
struct SegTree {
    int n, sz;
    vector<long long> node;
    function<ll(ll, ll)> op = [](ll a, ll b) {return max(a,b);}; //セグ木上の積
    ll e = 0; //セグ木上の積の単位元

    SegTree(int n_) {
        n = 1;
        while(n < n_) n *= 2;
        node.resize(2*n-1, e);
    }
    SegTree(vector<long long> a) {
        sz = a.size();

        n = 1;
        while(n < sz) n *= 2;
        node.resize(2*n-1, e);
        for(int i = 0; i < sz; i++) node[n-1+i] = a[i];
        for(int i = n-2; i >= 0; i--) node[i] = op(node[2*i+1], node[2*i+2]);
    }

    //a_iを取得
    long long get(int i) {
        return node[n-1+i];
    }
    //[a,b)における積
    long long prod(int a, int b, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;
        if(r <= a or b <= l) return e;
        if(a <= l and r <= b) return node[k];
        long long vl = prod(a, b, 2*k+1, l, (l+r)/2);
        long long vr = prod(a, b, 2*k+2, (l+r)/2, r);
        return op(vl, vr);
    }
    void update(int i, long long x) {
        i = n-1+i;
        node[i] = x;
        while(i > 0) {
            i = (i-1)/2;
            node[i] = op(node[2*i+1], node[2*i+2]);
        }
    }
    //[a,b)でa_i >= xなる最大のiを求める
    int maxindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        if(node[k] < x or r <= a or b <= l) return a-1;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vr = maxindex(a, b, x, 2*k+2, (l+r)/2, r);
            if(vr != a-1) return vr;
            else return maxindex(a, b, x, 2*k+1, l, (l+r)/2);
        }
    }
    //[a,b)でa_i >= xなる最小のiを求める
    int minindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        if(node[k] < x or r <= a or b <= l) return b;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vl = minindex(a, b, x, 2*k+1, l, (l+r)/2);
            if(vl != b) return vl;
            else return minindex(a, b, x, 2*k+2, (l+r)/2, r);
        }
    }
};
//遅延セグ木
struct LazySegTree {
    int n;
    vector<ll> node, lazy;
    const ll ex = 0, //セグ木上の積の単位元
             em = 0; //遅延評価木上の積の単位元
    function<ll(ll, ll)> fx = [](ll x, ll y) {return x+y;}, //セグ木上の積
                         fa = [](ll x, ll a) {return x+a;}, //遅延評価木からセグ木への作用
                         fm = [](ll a, ll b) {return a+b;}, //遅延評価木上の積
                         fp = [](ll a, ll n) {return a*n;}; //区間長の作用への影響

    LazySegTree(int n_) {
        n = 1;
        while(n < n_) n *= 2;
        node.resize(2*n-1, ex);
        lazy.resize(2*n-1, em);
    }
    LazySegTree(vector<ll> a) {
        int sz = a.size();
        
        n = 1;
        while(n < sz) n *= 2;
        node.resize(2*n-1, ex);
        lazy.resize(2*n-1, em);

        for(int i = 0; i < sz; i++) node[n-1+i] = a[i];
        for(int i = n-2; i >= 0; i--) node[i] = fx(node[2*i+1], node[2*i+2]);
    }

    void eval(int k, int len) {
        if(lazy[k] == em) return;

        node[k] = fa(node[k], fp(lazy[k], len));
        if(k < n-1) {
            lazy[2*k+1] = fm(lazy[2*k+1], lazy[k]);
            lazy[2*k+2] = fm(lazy[2*k+2], lazy[k]);
        }
        lazy[k] = em;
    }
    //区間[a,b)を値xに更新
    void update(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(r <= a or b <= l) return;
        if(a <= l and r <= b) {
            lazy[k] = fm(lazy[k], x);
            eval(k, r - l);
        }else {
            update(a, b, x, 2*k+1, l, (l+r)/2);
            update(a, b, x, 2*k+2, (l+r)/2, r);
            node[k] = fx(node[2*k+1], node[2*k+2]);
        }
    }
    //区間[a,b)の積を取得
    ll prod(int a, int b, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(r <= a or b <= l) return ex;
        if(a <= l and r <= b) return node[k];
        
        ll vl = prod(a, b, 2*k+1, l, (l+r)/2);
        ll vr = prod(a, b, 2*k+2, (l+r)/2, r);
        return fx(vl, vr);
    }
    //[a,b)でa_i >= xなる最大のiを求める
    int maxindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(node[k] < x or r <= a or b <= l) return a-1;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vr = maxindex(a, b, x, 2*k+2, (l+r)/2, r);
            if(vr != a-1) return vr;
            else return maxindex(a, b, x, 2*k+1, l, (l+r)/2);
        }
    }
    //[a,b)でa_i >= xなる最小のiを求める
    int minindex(int a, int b, long long x, int k = 0, int l = 0, int r = -1) {
        if(r < 0) r = n;

        eval(k, r - l);
        if(node[k] < x or r <= a or b <= l) return b;
        else if(k >= n-1) return (k - (n-1));
        else {
            int vl = minindex(a, b, x, 2*k+1, l, (l+r)/2);
            if(vl != b) return vl;
            else return minindex(a, b, x, 2*k+2, (l+r)/2, r);
        }
    }
};
//平方分割
struct Backet {
    vector<ll> array;
    vector<ll> backet;
    int N, M;

    Backet(vector<ll> a) {
        array = a;
        N = a.size(), M = ceil(sqrt(N));
        
        backet.resize((N+M-1)/M);
        for(int i = 0; i < N; i++) {
            backet[i/M] += a[i];
        }
    }
    void add(int i, ll x) {
        array[i] += x;
        backet[i/M] += x;
    }
    //区間[i,j)の和
    ll getsum(int i, int j) {
        int l = i/M + 1, r = j/M;
        ll s = 0;
        if(l > r) {
            rep(k,i,j) s += array[k];
        }else {
            rep(k,i,l*M) s += array[k];
            rep(k,l,r) s += backet[k];
            rep(k,r*M,j) s += array[k];
        }
        return s;
    }
};

void solve() {
}

int main() {
    int N; cin >> N;
    Graph G(N);
    rep(i,0,N) {
        int k; cin >> k;
        rep(j,0,k) {
            int c; cin >> c;
            G[i].push_back(c);
            G[c].push_back(i);
        }
    }

    vi depth(N, inf); depth[0] = 0;
    vi par(N, -1);
    function<void(int)> dfs = [&](int v) {
        for(auto next_v : G[v]) {
            if(depth[next_v] != inf) continue;
            depth[next_v] = depth[v]+1;
            par[next_v] = v;
            dfs(next_v);
        }
    };
    dfs(0);
    
    int M = 0;
    while((1<<M) < N) M++;
    vvi ancestor(M+1, vi(N));
    rep(i,0,N) {
        ancestor[0][i] = par[i];
    }
    rep(i,1,M) {
        rep(j,0,N) {
            if(ancestor[i-1][j] == -1) ancestor[i][j] = -1;
            else ancestor[i][j] = ancestor[i-1][ancestor[i-1][j]];
        }
    }

    auto anc = [&](int v, int g) {
        int k = 0;
        while(g > 0) {
            if(g % 2 == 1) v = ancestor[k][v];
            k++;
            g >>= 1;
        }
        return v;
    };

    int Q; cin >> Q;
    while(Q--) {
        int u, v; cin >> u >> v;
        int d1 = depth[u], d2 = depth[v];
        if(d1 > d2) u = anc(u, d1-d2);
        else v = anc(v, d2-d1);

        if(u == v) {
            cout << u << "\n";
            continue;
        }

        int k = M-1;
        while(par[u] != par[v]) {
            while(ancestor[k][u] == -1) k--;
            while(ancestor[k][u] == ancestor[k][v]) k--;
            u = ancestor[k][u];
            v = ancestor[k][v];
        }
        cout << par[u] << "\n";
    }

    return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 23052
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, e) for (int i = (int)(s); i < (int)(e); ++i)

struct LCA {
	vector<vector<int>> G, table;
	vector<int> dep;
	int N, K;
	
	LCA(vector<vector<int>> &g, int root = 0) : G(g), N(G.size()) {
		K = 1;
		while ((1 << K) < N) ++K;
		table = vector(K, vector<int>(N, -1));
		dep = vector<int>(N, -1);
		
		dfs(root, -1, 0);
		for (int k = 0; k < K - 1; ++k) {
			for (int v = 0; v < N; ++v) {
				if (table[k][v] < 0) table[k + 1][v] = -1;
				else table[k + 1][v] = table[k][table[k][v]];
			}
		}
	}
	
	void dfs(int v, int par, int d) {
		table[0][v] = par;
		dep[v] = d;
		for (int to : G[v]) {
			if (to != par) dfs(to, v, d + 1);
		}
	}
	
	int query(int u, int v) {
		if (dep[u] < dep[v]) swap(u, v);
		for (int k = 0; k < K; ++k) {
			if ((dep[u] - dep[v]) >> k & 1) u = table[k][u];
		}
		
		if (u == v) return u;
		for (int k = K - 1; k >= 0; --k) {
			if (table[k][u] != table[k][v]) {
				u = table[k][u];
				v = table[k][v];
			}
		}
		return table[0][u];
	}
};

int main() {
	cin.tie(nullptr);
	ios_base::sync_with_stdio(false);
	
	int N;
	cin >> N;
	vector<vector<int>> G(N);
	rep(v, 0, N) {
		int k;
		cin >> k;
		rep(j, 0, k) {
			int to;
			cin >> to;
			G[v].push_back(to);
			G[to].push_back(v);
		}
	}
	
	LCA lca(G);
	
	int Q;
	cin >> Q;
	rep(query, 0, Q) {
		int u, v;
		cin >> u >> v;
		cout << lca.query(u, v) << '\n';
	}
}
accuracy: 1
time_ms: 40
memory_kb: 27232
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;




vector<vector<int>> build(vector<int> par,int n)
{
    
    vector<vector<int>> dp(18,vector<int>(n,0));
    for(int i=0;i<n;i++)
    {
        dp[0][i]=par[i];

    }
    for(int i=1;i<=17;i++)
    {
        for(int u=0;u<n;u++)
        {
            int par=dp[i-1][u];
            dp[i][u]=dp[i-1][par];
        }
    }
    return dp;
}

int lca(int u,int v,vector<int> &dis,vector<vector<int>> &dp)
{
    if(dis[u]<dis[v])
    {
        swap(u,v);
    }
    int dif=dis[u]-dis[v];
    for(int i=17;i>=0;i--)
    {
        if(dif&(1<<i))
        {
            u=dp[i][u];
        }
    }
    if(u==v)
    return u;
    for (int i = 17; i >= 0; i--) {
    if (dp[i][u] != dp[i][v]) {
        u = dp[i][u];
        v = dp[i][v];
    }
}
return dp[0][u];

    // return u;
}



void dfs(int i,int par,int level,vector<vector<int>> &adj,vector<int> &p,vector<int> &dis)
{
    
    dis[i]=level;
    for(auto it:adj[i])
    {
        if(par!=it)
        {
            p[it]=i;
            dfs(it,i,level+1,adj,p,dis);
        }
    }
}



int main()
{
    int n;
    cin>>n;
    vector<vector<int>> adj(n);
    // vector<vector<int>> dp(18,vector<int>(n,0));

    for(int i=0;i<n;i++)
    {
        int sz=0;
        cin>>sz;
        for(int j=0;j<sz;j++)
        {
            int x;
            cin>>x;
            adj[i].push_back(x);
        }

    }
    vector<int> par(n,0);
    vector<int> dis(n,0);
    dfs(0,0,0,adj,par,dis);
    vector<vector<int>> dp=build(par,n);
    int q;
    cin>>q;
    for(int i=0;i<q;i++)
    {
        int x,y;
        cin>>x>>y;
        cout<<lca(x,y,dis,dp)<<endl;
    }


}
accuracy: 1
time_ms: 130
memory_kb: 24860
final_rank: 10

code_snippet: #include <bits/stdc++.h>

//#pragma GCC optimize("Ofast")
//#pragma GCC target("avx,avx2,fma")
//#pragma GCC optimization("unroll-loops")

using namespace std;

#define int long long
#define double long double
#define uint unsigned int
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define is2pow(n) (n && (!(n & (n-1))))
#define OPTIMIZE ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

const int MOD = 1e15 + 7;
const int MAX_N = 2e5;
const int MAX_K = 80;
const long long INF = 1e18;
const double EPS = 1e-6;

int binPow(int base, int pow, int mod) {
    if (pow == 0)
        return 1 % mod;
    int res = 1;
    while (pow != 0) {
        if (pow & 1)
            res = res * base % mod;
        base = base * base % mod;
        pow = pow >> 1;
    }
    return res;
}

struct LCA {
    vector<vector<int>> graph, dp;
    int n, maxK, root;
    vector<int> h;

    LCA(const vector<vector<int>>& g, int root = 0) :
        n(g.size()), root(root), graph(g), h(n), maxK(ceil(log2(n)) + 1) {
        dp.resize(n, vector<int>(maxK + 1, -1));
        dfs(root, root, 0);
        for (int k = 1; k <= maxK; k++)
            for (int u = 0; u < n; u++)
                if (dp[u][k - 1] != -1)
                    dp[u][k] = dp[dp[u][k - 1]][k - 1];
    }

    void dfs(int current, int parent, int height) {
        dp[current][0] = parent, h[current] = height;
        for (const int& to: graph[current])
            if (to != parent)
                dfs(to, current, height + 1);
    }

    int get(int u, int v) {
        if (h[u] > h[v])
            swap(u, v);
        for (int k = maxK; k >= 0; k--)
            if (h[u] <= h[v] - (1 << k))
                v = dp[v][k];
        if (u == v)
            return u;
        for (int k = maxK; k >= 0; k--)
            if (dp[u][k] != dp[v][k])
                u = dp[u][k], v = dp[v][k];
        return dp[u][0];
    }
};

void solve() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, q;
    cin >> n;
    vector<vector<int>> g(n);
    for (int i = 0, u, k; i < n; i++) {
        cin >> k;
        while (k--) {
            cin >> u;
            g[u].emplace_back(i);
            g[i].emplace_back(u);
        }
    }
    LCA lca(g, 0);
    cin >> q;
    while (q--) {
        int u, v;
        cin >> u >> v;
        cout << lca.get(u, v) << endl;
    }
}

#define TES
#define LOCA
#define FIL

int32_t main() {
    OPTIMIZE
#ifdef FILE
    freopen("path_easy.in", "r", stdin); freopen("path_easy.out", "w", stdout);
#endif
#ifdef LOCAL
    freopen("/home/rekname/data/C++ Projects/codedforceProject/input.txt", "r", stdin);
#endif
    int t = 1;
#ifdef TEST
    cin >> t;
#endif
    while (t--) {
        solve();
    }
}
accuracy: 1
time_ms: 90
memory_kb: 36620
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<(n);i++)
#define all(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()
#define YES cout<<"Yes"<<endl
#define NO cout<<"No"<<endl
using ll=long long;
using pi=pair<int,int>;
const int di[]={+1,-1,+0,+0};
const int dj[]={+0,+0,+1,-1};
const int INF=1e9;
//const ll INF=1e18;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n; cin >> n;
    vector<vector<int>> G(n);
    rep(i, n) {
        int k; cin >> k;
        rep(j, k) {
            int c; cin >> c;
            G[i].push_back(c);
        }
    }
    vector<int> dist(n, -1);
    dist[0]=0;
    const int LOG=20;
    vector<vector<int>> parent(LOG, vector<int>(n, -1));
    auto dfs=[&](auto dfs, int v, int p=-1) -> void {
        parent[0][v]=p;
        for(int nv:G[v]) {
            dist[nv]=dist[v]+1;
            dfs(dfs, nv, v);
        }
    };
    dfs(dfs, 0);
    rep(k, LOG-1) {
        rep(v, n) {
            if(parent[k][v]<0) parent[k+1][v]=-1;
            else parent[k+1][v]=parent[k][parent[k][v]];
        }
    }
    auto lca=[&](int u, int v) -> int {
        if(dist[u]<dist[v]) swap(u, v);
        int diff=dist[u]-dist[v];
        rep(k, LOG) if(diff>>k&1) u=parent[k][u];
        if(u==v) return u;
        for(int k=LOG-1; k>=0; k--) {
            if(parent[k][u]!=parent[k][v]) {
                u=parent[k][u];
                v=parent[k][v];
            }
        }
        return parent[0][u];
    };
    int q; cin >> q;
    vector<int> ans;
    while(q--) {
        int u, v; cin >> u >> v;
        ans.push_back(lca(u, v));
    }
    for(int x:ans) cout << x << endl;

    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 25360
final_rank: 7

code_snippet: #include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <iomanip>
#include <numeric>
#include <queue>
#include <stack>
using namespace std;
using ll = long long;
struct LCA{
	vector<vector<int>> par;
	vector<int> dist;
	int K = 60;
	void dfs(int v, int lat, int d, vector<vector<int>>& G) {
		dist[v] = d;
		par[0][v] = lat;
		for (auto nv : G[v]) {
			if (nv == lat) continue;
			dfs(nv, v, d+1, G);
		}
	}
	LCA(vector<vector<int>>& G) {
		int N = G.size();
		par.assign(K+1, vector<int>(N, -1));
		dist.assign(N, 0);
		dfs(0, 0, 0, G);
		for (int k = 1;k <= K;k++) for (int v = 0;v < N;v++) par[k][v] = par[k-1][par[k-1][v]];
	}
	int query(int u, int v) {
		if (dist[u] < dist[v]) swap(u, v);
		ll df = dist[u] - dist[v];
		for (int k = 0;k <= K;k++) if (df & (1LL << k)) u = par[k][u];
		if (u == v) return u;
		for (int k = K;k >=0;k--) {
			if (par[k][u] != par[k][v]) {
				u = par[k][u];
				v = par[k][v];
			}
		}
		return par[0][u];
	}
};
int main() {
	int N;cin >> N;
	vector<vector<int>> G(N);
	for (int i = 0;i < N;i++) {
		int k;cin >> k;
		while (k--) {
			int c;cin >> c;
			G[i].push_back(c);
			G[c].push_back(i);
		}
	}
	LCA LCAS(G);
	int Q;cin >> Q;
	while (Q--) {
		int u, v;cin >> u >> v;
		cout << LCAS.query(u, v) << endl;
	}
	
}
accuracy: 1
time_ms: 200
memory_kb: 40588
final_rank: 18

code_snippet: #include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <iomanip>
#include <numeric>
#include <queue>
#include <stack>
using namespace std;
using ll = long long;
using Graph = vector<vector<int>>;
struct LCA {
	vector<int> dist;
	vector<vector<int>> par;
	int K = 60;
	void dfs(int v, int lat, int d, Graph& G) {
		dist[v] = d;
		par[0][v] = lat;
		for (auto nv : G[v]) {
			if (nv == lat) continue;
			dfs(nv, v, d+1, G);
		}
	}
	LCA (Graph& G) {
		int N = G.size();
		par.assign(K+1, vector<int>(N, -1));
		dist.assign(N, 0);
		dfs(0, 0, 0, G);
		for (int k = 1;k <= K;k++) for (int v = 0;v < N;v++) par[k][v] = par[k-1][par[k-1][v]];
	}

	int query(int u, int v) {
		if (dist[u] < dist[v]) swap(u, v);
		ll d = dist[u] - dist[v];
		for (int k = 0;k <= K;k++) {
			if (d & (1LL << k)) u = par[k][u];
		}
		if (u == v) return u;
		for (int k = K;k >= 0;k--) {
			if (par[k][u] != par[k][v]) {
				u = par[k][u];
				v = par[k][v];
			}
		}
		return par[0][u];
	}
};
int main() {
	int N;cin >> N;
	Graph G(N);
	for (int i = 0;i < N;i++) {
		int k;cin >> k;
		while (k--) {
			int c;cin >> c;
			G[i].push_back(c);
			G[c].push_back(i);
		}
	}
	LCA LCAS(G);
	int Q;cin >> Q;
	while (Q--) {
		int u, v;cin >> u >> v;
		cout << LCAS.query(u, v) << endl;
	}
}
accuracy: 1
time_ms: 200
memory_kb: 40672
final_rank: 19

code_snippet: #include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <iomanip>
#include <numeric>
#include <queue>
#include <stack>
using namespace std;
using ll = long long;
using Graph = vector<vector<int>>;
struct LCA {
	int K = 60;
	vector<vector<int>> par;
	vector<int> dist;
	void dfs(int v, int lat, int d, vector<vector<int>>& G) {
		par[0][v] = lat;
		dist[v] = d;
		for (auto nv : G[v]) {
			if (nv == lat) continue;
			dfs(nv, v, d+1, G);
		}
	}
	LCA (Graph G) {
		int N = G.size();
		par.assign(K+1, vector<int>(N, -1));
		dist.assign(N, 0);
		dfs(0, 0, 0, G);
		for (int k = 1;k <= K;k++) for (int v = 0;v < N;v++) par[k][v] = par[k-1][par[k-1][v]];
	}
	int query(int u, int v) {
		if (dist[u] < dist[v]) swap(u, v);
		ll df = dist[u] - dist[v];
		for (int k = 0;k <= K;k++) {
			if ((df)  & (1LL << k)) {
				u = par[k][u];
				
			}
		}
		if (u == v) return u;
		for (int k = K;k >= 0;k--) {
			if (par[k][u] != par[k][v]) {
				u = par[k][u];
				v = par[k][v];
			}
		}
		return par[0][u];
	}
};
int main() {
	int N;cin >> N;
	Graph G(N);
	for (int i = 0;i < N;i++) {
		int k;cin >> k;
		while (k--) {
			int c;cin >> c;
			G[i].push_back(c);
			G[c].push_back(i);
		}
	}
	LCA LCAS(G);
	int Q;cin >> Q;
	while (Q--) {
		int u, v;cin >> u >> v;
		cout << LCAS.query(u, v) << endl;
	}
}
accuracy: 1
time_ms: 200
memory_kb: 46100
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll=long long;

class LCA{
    vector<vector<int>> db;
    vector<int> dist;

public:
    LCA(const vector<vector<int>>& G,int root){
        int N=G.size(),K=0;
        while((1<<K)<N) K++;

        db.assign(N,vector<int>(K,-1));
        dist.assign(N,-1);
        queue<int> que;
        dist[root]=0;
        que.push(0);
        while(!que.empty()){
            int cv=que.front(); que.pop();
            for(int nv:G[cv]){
                if(dist[nv]!=-1) continue;
                db[nv][0]=cv;
                dist[nv]=dist[cv]+1;
                que.push(nv);
            }
        }

        for(int i=1;i<K;i++){
            for(int j=0;j<N;j++){
                if(db[j][i-1]==-1) db[j][i]=-1;
                else db[j][i]=db[db[j][i-1]][i-1];
            }
        }
    }

    int query(int u,int v){
        if(dist[u]<dist[v]) swap(u,v);
        for(int i=0;dist[u]>dist[v];i++){
            if((dist[u]-dist[v])&(1<<i)) u=db[u][i];
        }

        if(u==v) return u;
        for(int i=db[0].size()-1;i>=0;i--){
            if(db[u][i]!=db[v][i]){
                u=db[u][i];
                v=db[v][i];
            }
        }
        return db[u][0];
    }
};

int main(){
    int N; cin>>N;
    vector<vector<int>> G(N);
    for(int i=0;i<N;i++){
        int k; cin>>k;
        for(int j=0;j<k;j++){
            int c; cin>>c;
            G[i].push_back(c);
        }
    }
    LCA lca(G,0);

    int Q; cin>>Q;
    for(int i=0;i<Q;i++){
        int u,v; cin>>u>>v;
        cout<<lca.query(u,v)<<endl;
    }
}
accuracy: 1
time_ms: 150
memory_kb: 19076
final_rank: 9

code_snippet: #include <bits/stdc++.h>
//#include <windows.h>
//#include <conio.h>
#define N 600001
#define M 23
#define CODE ""
#define MOD
#define F first
#define S second
#define pb push_back
#define eb emplace_back
#define is insert
#define pii pair<int,int>
#define pll pair<long long,long long>
#define mkp make_pair
#define ll [](long long &ctrl)
typedef int ui;
typedef long long ul;
typedef long double ud;
using namespace std;
const int iinf=(int)1e9+7;
const long long linf=(long long)1e18+7;
ui n,q,h[N],up[N][M];
vector<ui> adj[N];
void dfs(ui s,ui pre)
{
    for(ui i=0;i<(ui)adj[s].size();i++)
    {
        ui ctrl=adj[s][i];
        if(ctrl!=pre)
        {
            h[ctrl]=h[s]+1;
            up[ctrl][0]=s;
            for(ui j=1;j<=__lg(n);j++)
                up[ctrl][j]=up[up[ctrl][j-1]][j-1];
            dfs(ctrl,s);
        }
    }
}
ui lca(ui u,ui v)
{
    if(h[u]!=h[v])
    {
        if(h[u]<h[v]) swap(u,v);
        ui k=h[u]-h[v];
        for(ui i=0;(1<<i)<=k;i++)
            if(k>>i&1) u=up[u][i];
    }
    if(u==v) return u;
    ui k=__lg(h[u]);
    for(ui i=k;i>=0;i--)
        if(up[u][i]!=up[v][i])
        {
            u=up[u][i];
            v=up[v][i];
        }
    return up[u][0];
}
int32_t main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    #ifdef CODE
    if(fopen(CODE".INP","r"))
    {
        freopen(CODE".INP","r",stdin);
        freopen(CODE".OUT","w",stdout);
    }
    #endif // CODE
    #define __FILE_CHECK__
    #ifdef __FILE_CHECK__
    if(fopen("inp.txt","r"))
    {
       freopen("inp.txt","r",stdin);
       freopen("out.txt","w",stdout);
       freopen("opu.txt","w",stderr);
    }
    #endif // __FILE_CHECK__
    cin>>n;
    for(ui i=0;i<n;i++)
    {
        ui u;
        cin>>u;
        for(ui j=1;j<=u;j++)
        {
            ui v;
            cin>>v;
            adj[i].pb(v);
            adj[v].pb(i);
        }
    }
    dfs(0,-1);
    cin>>q;
    while(q--)
    {
        ui u,v;
        cin>>u>>v;
        cout<<lca(u,v)<<"\n";
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 39316
final_rank: 6

code_snippet: #line 1 "a.cpp"
#define PROBLEM \
  "https://onlinejudge.u-aizu.ac.jp/courses/library/5/GRL/5/GRL_5_C"
#line 2 "/workspaces/cp-container/libraries/template.hpp"
#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < (int)(n); ++i)
using ll = long long;
template <typename T>
bool chmax(T& a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
template <typename T>
bool chmin(T& a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
#line 3 "/workspaces/cp-container/libraries/data_structure/sparse_table.hpp"

template <class S, S (*op)(S, S), S (*e)()>
struct sparse_table {
  int n;
  vector<vector<S>> st;

  sparse_table() : n(0) {}
  sparse_table(vector<S> v) : n(v.size()) {
    if (n == 0) return;
    int log_n = 0;
    while ((1 << log_n) <= n) ++log_n;
    st.assign(log_n, vector<S>(n));

    st[0] = v;
    rep(k, log_n - 1) {
      for (int i = 0; i + (1 << (k + 1)) <= n; ++i) {
        st[k + 1][i] = op(st[k][i], st[k][i + (1 << k)]);
      }
    }
  }

  S prod(int l, int r) {
    if (l >= r) return e();
    int k = 31 - __builtin_clz(r - l);
    return op(st[k][l], st[k][r - (1 << k)]);
  }
};
#line 4 "/workspaces/cp-container/libraries/graph/lowest_common_ancestor.hpp"

struct lowest_common_ancestor {
  using P = pair<int, int>;
  static P op(P a, P b) { return min(a, b); }
  static P e() { return {1 << 30, -1}; }

  int n;
  vector<vector<int>> g;
  vector<int> depth, pos;
  vector<P> tour;
  sparse_table<P, op, e> st;

  lowest_common_ancestor(vector<vector<int>> _g, int root = 0)
      : n(_g.size()), g(_g), depth(_g.size()), pos(_g.size()) {
    dfs(root, -1, 0);
    st = sparse_table<P, op, e>(tour);
  }

  void dfs(int u, int p, int d) {
    depth[u] = d;
    pos[u] = tour.size();
    tour.emplace_back(d, u);
    for (int v : g[u]) {
      if (v == p) continue;
      dfs(v, u, d + 1);
      tour.emplace_back(d, u);
    }
  }

  int query(int u, int v) {
    int l = pos[u];
    int r = pos[v];
    if (l > r) swap(l, r);
    return st.prod(l, r + 1).second;
  }

  int dist(int u, int v) {
    int l = query(u, v);
    return depth[u] + depth[v] - 2 * depth[l];
  }
};
#line 5 "a.cpp"
signed main() {
  cin.tie(0)->sync_with_stdio(0);
  int n;
  cin >> n;
  vector<vector<int>> g(n);
  rep(u, n) {
    int k;
    cin >> k;
    rep(_, k) {
      int v;
      cin >> v;
      g[u].emplace_back(v);
      g[v].emplace_back(u);
    }
  }
  lowest_common_ancestor lca(g);
  int q;
  cin >> q;
  rep(_, q) {
    int u, v;
    cin >> u >> v;
    cout << lca.query(u, v) << '\n';
  }
}
accuracy: 1
time_ms: 40
memory_kb: 57424
final_rank: 17

code_snippet: #include <vector>
#include <cmath>

struct Edge {
    int from;
    int to;
    int weight{1};
    int index{};
    int id{};
    long long capacity{};
    long long flow{};
    Edge(int from, int to) : from(from), to(to) {}
    Edge(int from, int to, int weight) : from(from), to(to), weight(weight) {}
    Edge(int from, int to, int capacity, int flow) : from(from), to(to), capacity(capacity), flow(flow) {}

    bool operator>(const Edge& other) const { return weight > other.weight; }
};


using namespace std;

struct LCA {
    // LCA with Binary Lifting
    // Time  : Preprocessing - O(n * log(n)), Query - O(log n)
    // Space : O(n * log(n))
    vector<vector<Edge>> graph;
    int vertexCount;
    const int root;
    vector<vector<int>> dp;
    vector<int> height;
    int maxK;
    bool built;

    LCA(const vector<vector<int>>& g, int root = 0) : vertexCount(g.size()), root(root) {
        graph.resize(vertexCount);
        for (int from = 0; from < vertexCount; from++)
            for (int to: g[from])
                addEdge(from, to);
        maxK = 0;
        while ((1 << maxK) <= vertexCount)
            maxK++;
        dp.resize(vertexCount, vector<int>(maxK + 1, -1));
        height.resize(vertexCount);
        build();
    }

    LCA(int vertexCount, int root = 0) : vertexCount(vertexCount), root(root), built(false) {
        graph.resize(vertexCount);
        maxK = ceil(log2(vertexCount)) + 1;
        dp.resize(vertexCount, vector<int>(maxK + 1, -1));
        height.resize(vertexCount);
    }

    void addEdge(int from, int to, int weight = 1) {
        graph[from].emplace_back(from, to, weight);
        graph[to].emplace_back(to, from, weight);
    }

    void build() {
        if (built)
            return;
        dfs(root, root, 0);
        for (int k = 1; k <= maxK; k++)
            for (int u = 0; u < vertexCount; u++)
                if (dp[u][k - 1] != -1)
                    dp[u][k] = dp[dp[u][k - 1]][k - 1];
        built = true;
    }

    void dfs(int current, int parent, int h) {
        dp[current][0] = parent;
        height[current] = h;
        for (const Edge& edge: graph[current]) {
            if (edge.to != parent)
                dfs(edge.to, current, h + 1);
        }
    }

    int get(int u, int v) {
        if (!built)
            build();
        if (height[u] > height[v])
            swap(u, v);
        for (int k = maxK; k >= 0; k--)
            if (height[u] <= height[v] - (1 << k))
                v = dp[v][k];
        if (u == v)
            return u;

        for (int k = maxK; k >= 0; k--) {
            if (dp[u][k] != dp[v][k]) {
                u = dp[u][k];
                v = dp[v][k];
            }
        }
        return dp[u][0];
    }
};

#include <bits/stdc++.h>
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/library/5/GRL/all/GRL_5_C"

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, q;
    cin >> n;
    LCA lca(n, 0);
    for (int i = 0, u, k; i < n; i++) {
        cin >> k;
        while (k--) {
            cin >> u;
            lca.addEdge(u, i);
        }
    }
    cin >> q;
    while (q--) {
        int u, v;
        cin >> u >> v;
        cout << lca.get(u, v) << endl;
    }
}
accuracy: 1
time_ms: 120
memory_kb: 33172
final_rank: 15

code_snippet: #include <iostream>
#include <vector>

using namespace std;

const int max_nodes = 100005, log_max_nodes = 20;
int num_nodes, log_num_nodes, root;

vector<int> children[max_nodes];
int A[max_nodes][log_max_nodes+1];
int L[max_nodes];

int lb(unsigned int n)
{
    if(n == 0) return -1;
    int p = 0;
    if(n >= (1 << 16)) { n >>= 16; p += 16; }
    if(n >= (1 << 8)) { n >>= 8; p += 8; }
    if(n >= (1 << 4)) { n >>= 4; p += 4; }
    if(n >= (1 << 2)) { n >>= 2; p += 2; }
    if(n >= (1 << 1)) { p += 1; }
    return p;
}

void DFS(int i, int l)
{
    L[i] = l;
    for(int j = 0; j < children[i].size(); j++)
        DFS(children[i][j], l + 1);
}

int LCA(int p, int q)
{
    if(L[p] < L[q]) swap(p, q);

    for(int i = log_num_nodes; i >= 0; i--){
        if(L[p] - (1 << i) >= L[q]) p = A[p][i];
    }
    if(p == q) return p;
    for(int i = log_num_nodes; i >= 0; i--){
        if(A[p][i] != -1 && A[p][i] != A[q][i]){
            p = A[p][i];
            q = A[q][i];
        }
    }
    return A[p][0];
}

bool findroot[max_nodes] = {false};

int main()
{
    int n;
    cin >> n;
    num_nodes = n;
    log_num_nodes = lb(num_nodes);
    for(int i = 0; i < n; ++i){
        int k;
        cin >> k;
        int now;
        for(int j = 0; j < k; ++j){
            cin >> now;
            children[i].push_back(now);
            A[now][0] = i;
            findroot[now] = true;
        }
    }
    int root;
    for(int i = 0; i < n; ++i){
        if(!findroot[i]){
            root = i;
            break;
        }
    }
    for(int j = 1; j <= log_num_nodes; ++j){
        for(int i = 0; i < num_nodes; ++i){
            if(A[i][j - 1] != -1){
                A[i][j] = A[A[i][j - 1]][j - 1];
            } else {
                A[i][j] = -1;
            }
        }
    }
    DFS(root, 0);
    int q;
    cin >> q;
    int u, v;
    for(int i = 0; i < q; ++i){
        cin >> u >> v;
        cout << LCA(u, v) << endl;
    }
}
accuracy: 1
time_ms: 140
memory_kb: 20424
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < (n); ++i)

int main() {
    int n;
    cin >> n;
    vector<vector<int>> g(n);
    rep(i, n) {
        int k;
        cin >> k;
        rep(j, k) {
            int c;
            cin >> c;
            g[i].push_back(c);
            g[c].push_back(i);
        }
    }

    int K = 1;
    while ((1 << K) < n) K++;
    vector<vector<int>> parent(K, vector<int>(n, -1));
    vector<int> dist(n, -1);
    auto dfs = [&](auto dfs, int v, int d, int p = -1) -> void {
        parent[0][v] = p;
        dist[v] = d;
        for (auto nv : g[v])
            if (nv != p) dfs(dfs, nv, d + 1, v);
    };
    dfs(dfs, 0, 0);
    rep(k, K - 1) rep(v, n) if (parent[k][v] < 0) parent[k + 1][v] = -1;
    else parent[k + 1][v] = parent[k][parent[k][v]];
    auto query = [&](int u, int v) -> int {
        if (dist[u] < dist[v]) swap(u, v);
        rep(k, K) if ((dist[u] - dist[v]) >> k & 1) u = parent[k][u];
        if (u == v) return u;
        for (int k = K - 1; k >= 0; k--)
            if (parent[k][u] != parent[k][v])
                u = parent[k][u], v = parent[k][v];
        return parent[0][u];
    };

    int q;
    cin >> q;
    rep(iq, q) {
        int u, v;
        cin >> u >> v;
        cout << query(u, v) << endl;
    }
}
accuracy: 1
time_ms: 140
memory_kb: 23320
final_rank: 12

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("avx,avx2")
#include <bits/stdc++.h>
#define INF 1000000001LL
#define LNF 1000000000000000001LL
#define MOD 1000000007LL
#define MAX 200000
#define long long long
#define all(x) x.begin(),x.end()
using namespace std;

int dp[100000][20];
int depth[100000];
vector<int> graph[100000];
void dfs(int x, int d)
{
    depth[x] = d;
    for(int y : graph[x])
        dfs(y,d+1);
}

int lca(int x, int y)
{
    if(depth[x] < depth[y])
        swap(x,y);
    int diff = depth[x]-depth[y];

    for(int i = 0; i<20; i++)
    {
        if(diff&(1<<i))
            x = dp[x][i];
    }

    if(x == y)
        return x;

    for(int i = 19; i>=0; i--)
    {
        if(dp[x][i] != dp[y][i])
        {
            x = dp[x][i];
            y = dp[y][i];
        }
    }
    return dp[x][0];
}

int main()
{
	ios_base::sync_with_stdio(0); 
    cin.tie(0);

    int n;
    cin >> n;

    for(int i = 0; i<n; i++)
    {
        int m;
        cin >> m;

        for(int j = 0; j<m; j++)
        {
            int x;
            cin >> x;
            dp[x][0] = i;
            graph[i].push_back(x);
        }
    }

    for(int k = 1; k<20; k++)
    {
        for(int i = 0; i<n; i++)
        {
            dp[i][k] = dp[dp[i][k-1]][k-1];
        }
    }

    dfs(0,0);

    int q;
    cin >> q;

    while(q--)
    {
        int a,b;
        cin >> a >> b;
        cout << lca(a,b) << "\n";
    }


    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 21748
final_rank: 1

code_snippet: #include<bits/stdc++.h>
//#include <atcoder/all>
using namespace std;
//using namespace atcoder;

#define all(v) v.begin(),v.end()
using ll = long long;
using ull = unsigned long long;
using lll = __int128;
using vll=vector<ll>;
using vvll = vector<vector<ll>>;
using P = pair<ll,ll>;
using vp=vector<pair<ll, ll>>;
//using mint=modint1000000007;
//using mint=modint998244353;

const ll INF=1ll<<60;
ll mod10=1e9+7;
ll mod99=998244353;
const double PI = acos(-1);

#define rep(i,n) for (ll i=0;i<n;++i)
#define per(i,n) for(ll i=n-1;i>=0;--i)
#define rep2(i,a,n) for (ll i=a;i<n;++i)
#define per2(i,a,n) for (ll i=a;i>=n;--i)

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

template<typename Monoid, typename Operator, auto mapping>
struct SegTreeLazy {
    using MonoidType=typename Monoid::Type;
    using OperatorType=typename Operator::Type;
    SegTreeLazy()=default;

    /// @brief 要素数 n の遅延セグ木を構築する
    SegTreeLazy(int n) {
        this->n=n;
        dat=vector<MonoidType>(n<<1,Monoid::id());
        lazy=vector<OperatorType>(n<<1,Operator::id());
    }

    /// @brief 配列 v から遅延セグ木を構築する
    SegTreeLazy(const vector<MonoidType>& v) {
        this->n=v.size();
        dat=vector<MonoidType>(n<<1,Monoid::id());
        lazy=vector<OperatorType>(n<<1,Operator::id());
        for(int i=0; i<n; i++) dat[i+n]=v[i];
        for(int i=n-1; i>0; i--) dat[i]=Monoid::op(dat[i<<1],dat[i<<1|1]);
    }

    /// @brief i 番目の要素を x に更新する
    void set(int i, MonoidType x) {
        generate_indices(i,i+1);
        pushdown();
        i+=n;
        dat[i]=x;
        while(i>>=1) dat[i]=Monoid::op(dat[i<<1],dat[i<<1|1]);
    }

    /// @brief 区間 [l, r) に x を作用させる
    void apply(int l, int r, OperatorType x) {
        if(l==r) return;
        generate_indices(l,r);
        pushdown();
        l+=n; r+=n;
        while(l<r) {
            if(l&1) {
                lazy[l]=Operator::op(lazy[l],x);
                dat[l]=mapping(dat[l],x);
                l++;
            }
            if(r&1) {
                r--;
                lazy[r]=Operator::op(lazy[r],x);
                dat[r]=mapping(dat[r],x);
            }
            l>>=1; r>>=1;
        }
        pushup();
    }

    /// @brief 区間 [l, r) のモノイド積を返す
    MonoidType fold(int l, int r) {
        if(l==r) return Monoid::id();
        generate_indices(l,r);
        pushdown();
        MonoidType retl=Monoid::id(),retr=Monoid::id();
        l+=n; r+=n;
        while(l<r) {
            if(l&1) retl=Monoid::op(retl,dat[l++]);
            if(r&1) retr=Monoid::op(dat[--r],retr);
            l>>=1; r>>=1;
        }
        return Monoid::op(retl,retr);
    }

    /// @brief 区間 [l, x) のモノイド積が f を満たすような最大の x >= l を返す
    /// @attention `f(Monoid::id())=true` が成り立つ必要がある
    /// @note O(log(N))
    template<typename F>
    int find_right(int l, F f) {
        assert(f(Monoid::id()));
        if(l==n) return n;
        generate_indices(l,n);
        pushdown();
        l+=n;
        int r=n+n;
        vector<int> cand_l,cand_r;
        while(l<r) {
            if(l&1) cand_l.push_back(l++);
            if(r&1) cand_r.push_back(--r);
            l>>=1; r>>=1;
        }
        vector<int> cand=cand_l;
        reverse(cand_r.begin(),cand_r.end());
        cand.insert(cand.end(),cand_r.begin(),cand_r.end());
        MonoidType val=Monoid::id();
        for(int i:cand) {
            if(f(Monoid::op(val,dat[i]))) {
                val=Monoid::op(val,dat[i]);
            } else {
                while(i<n) {
                    propagate(i);
                    i<<=1;
                    if(f(Monoid::op(val,dat[i]))) {
                        val=Monoid::op(val,dat[i]);
                        i|=1;
                    }
                }
                return i-n;
            }
        }
        return n;
    }

    /// @brief 区間 [x, r) のモノイド積が f を満たすような最小の x<=r を返す
    /// @attention `f(Monoid::id())=true` が成り立つ必要がある
    /// @note O(log(N))
    template<typename F>
    int find_left(int r, F f) {
        assert(f(Monoid::id()));
        if(r==0) return 0;
        generate_indices(0,r);
        pushdown();
        r+=n;
        int l=n;
        vector<int> cand_l,cand_r;
        while(l<r) {
            if(l&1) cand_l.push_back(l++);
            if(r&1) cand_r.push_back(--r);
            l>>=1; r>>=1;
        }
        vector<int> cand=cand_r;
        reverse(cand_l.begin(),cand_l.end());
        cand.insert(cand.end(),cand_l.begin(),cand_l.end());
        MonoidType val=Monoid::id();
        for(int i:cand) {
            if(f(Monoid::op(dat[i],val))) {
                val=Monoid::op(dat[i],val);
            } else {
                while(i<n) {
                    propagate(i);
                    i=(i<<1)|1;
                    if(f(Monoid::op(dat[i],val))) {
                        val=Monoid::op(dat[i],val);
                        i^=1;
                    }
                }
                return i-n+1;
            }
        }
        return 0;
    }

    int size() { return n; }
    MonoidType operator[](int i) { return fold(i,i+1); }

private:
    int n;
    vector<MonoidType> dat;
    vector<OperatorType> lazy;
    vector<int> indices;
    void generate_indices(int l, int r) {
        indices.clear();
        l+=n; r+=n;
        int lm=(l/(l&-l))>>1,rm=(r/(r&-r))>>1;
        while(l<r) {
            if(l<=lm) indices.push_back(l);
            if(r<=rm) indices.push_back(r);
            l>>=1; r>>=1;
        }
        while(l) {
            indices.push_back(l);
            l>>=1;
        }
    }
    void propagate(int i) {
        if(i<n) {
            lazy[i<<1]=Operator::op(lazy[i<<1],lazy[i]);
            lazy[i<<1|1]=Operator::op(lazy[i<<1|1],lazy[i]);
            dat[i<<1]=mapping(dat[i<<1],lazy[i]);
            dat[i<<1|1]=mapping(dat[i<<1|1],lazy[i]);
        }
        lazy[i]=Operator::id();
    }
    void pushdown() {
        for(int j=(int)indices.size()-1; j>=0; j--) {
            int i=indices[j];
            propagate(i);
        }
    }
    void pushup() {
        for(int j=0; j<(int)indices.size(); j++) {
            int i=indices[j];
            dat[i]=Monoid::op(dat[i<<1],dat[i<<1|1]);
        }
    }
};


/// @brief モノイド
namespace Monoid {

 
    /// @brief （和，区間の長さ）
    template<typename T>
    struct SumPair {
        using Type=pair<T,int>;
        static Type id() { return make_pair(T(0),0); }
        static Type op(const Type& a, const Type& b) { return {(a.first+b.first),a.second+b.second}; }
    };
}


/// @brief 作用素
namespace Operator {
    /// @brief 更新
    template<typename T, T not_exist>
    struct Update {
        using Type=T;
        static Type id() { return not_exist; }
        static Type op(const Type& a, const Type& b) { return (b==id()?a:b); }
    };

  
}


namespace RangeQuery {


    /// @brief 区間更新 / 区間和
    /// @tparam not_exist 存在しない値
    template<typename T, T not_exist>
    struct ApplyUpdate_GetSum {
        using S=typename Monoid::SumPair<T>::Type;
        static S mapping(const S& a, const T& b) { return b==not_exist?a:S{b,a.second}; }
        using Type=struct SegTreeLazy<Monoid::SumPair<T>,Operator::Update<T,not_exist>,mapping>;
    };

 
   

  
}




//----------------------------------------------------------





struct HLD{
    int N;
    vector<int> si,par,head,dep;
    vector<int> hld;//ならべたやつ
    vector<int> idx;//hldの何番目にいるか
    vector<int> L,R;//[L[i],R[i])でiの部分木がとれる
    vector<vector<long long>>& ab;

    void dfs1(ll a,ll b){
        for(int i=0;i<ab[a].size();i++){
            int to=ab[a][i];
            if(to==b) continue;
            dep[to]=dep[a]+1;
            dfs1(to,a);
            si[a]+=si[to];
        }
        
        for(int i=0;i<ab[a].size();i++){
            if((ab[a][0]==b)||(ab[a][i]!=b&&si[ab[a][0]]<si[ab[a][i]])) swap(ab[a][0],ab[a][i]);
        }
    }

    void dfs2(ll a,ll b,ll h){
        idx[a]=hld.size();
        L[a]=hld.size();
        hld.emplace_back(a);
        head[a]=h;
        par[a]=b;
        for(int i=0;i<ab[a].size();i++){
            if(ab[a][i]==b) continue;
            if(i==0) dfs2(ab[a][i],a,h);
            else dfs2(ab[a][i],a,ab[a][i]);
        }
        R[a]=hld.size();
    }

    HLD(vector<vector<long long>>& ab2):ab(ab2){
        N=ab2.size();
        si.assign(N,1);
        par.assign(N,0);
        head.assign(N,0);
        dep.assign(N,0);
        idx.assign(N,0);
        L.assign(N,0);
        R.assign(N,0);
        dfs1(0,-1);   
        dfs2(0,-1,0);
    }

    int lca(int a,int b){
        while(head[a]!=head[b]){
            if(dep[head[a]]>dep[head[b]]) swap(a,b);
            b=par[head[b]];     
        }
        return (dep[a]<dep[b]?a:b);
    }

    //a-bの辺をどのindexにsetすればいいかかえす 子から親に辺を張る
    int edgeid(ll a,ll b){
        if(dep[a]>dep[b]) swap(a,b);
        return idx[b];
    }
    //辺に対するクエリ　子から親に辺を張る
    void edgequery(ll a,ll b,vector<pair<ll,ll>>& R){
        while(head[a]!=head[b]){
            
            if(dep[head[a]]>dep[head[b]]) swap(a,b);
            R.emplace_back(idx[head[b]],idx[b]+1);
            b=par[head[b]];
        }
        if(dep[a]>dep[b]) swap(a,b);
        R.emplace_back(idx[a]+1,idx[b]+1);
    }

    //頂点をsetするときのindex
    int nodeid(ll a){
        return idx[a];
    }

    void nodequery(ll a,ll b,vector<pair<ll,ll>>& R){
        while(head[a]!=head[b]){
            if(dep[head[a]]>dep[head[b]]) swap(a,b);
            R.emplace_back(idx[head[b]],idx[b]+1);
            b=par[head[b]];
        }
        if(dep[a]>dep[b]) swap(a,b);
        R.emplace_back(idx[a],idx[b]+1);
    }

};


bool solve(){
    ll N;cin>>N;
  
    vvll ab(N);
    
    rep(i,N){
        ll k;cin>>k;
        rep(j,k){
            ll a;cin>>a;
            ab[a].emplace_back(i);
            ab[i].emplace_back(a);
        }
    }

    HLD hld(ab);
    ll Q;cin>>Q;

    rep(i,Q){
        ll u,v;cin>>u>>v;
        cout<<hld.lca(u,v)<<endl;

    }
    return 0;
}

  
    
    


int main(){
    cin.tie(0);
    ios::sync_with_stdio(false);
    ll T=1;//cin>>T;
    rep(i,T) solve();
}
accuracy: 1
time_ms: 100
memory_kb: 19784
final_rank: 4

code_snippet: // https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_5_C&lang=en
#include <cassert>
#include <cstdio>
#include <vector>

using namespace std;

class Node {
public:
    vector<int> child;
    vector<int> pow2_ancestor;
    int depth;
    bool is_visited;
    Node()
    {
        depth = -1;
        is_visited = false;
    }
};

void dfs(vector<Node>& tree, vector<int>& parent, int cur)
{
    if (tree.at(cur).is_visited)
    {
        return;
    }
    tree.at(cur).is_visited = true;
    int depth = (int)parent.size();
    tree.at(cur).depth = depth;

    for (int i = 0; (1 << i) <= depth; i++)
    {
        tree.at(cur).pow2_ancestor.push_back(parent.at(depth - (1 << i)));
    }

    parent.push_back(cur);
    for (auto x : tree.at(cur).child)
    {
        dfs(tree, parent, x);
    }
    parent.pop_back();
}

int get_nth_ancestor(const vector<Node>& tree, int cur, int nth)
{
    for (int i = 0; (1 << i) <= nth; i++)
    {
        if ((1 << i) & nth)
        {
            cur = tree.at(cur).pow2_ancestor.at(i);
        }
    }
    return cur;
}

void record_ancestor(vector<Node>& tree)
{
    vector<int> parent;
    dfs(tree, parent, 0);
}

int compute_lca(const vector<Node>& tree, int u, int v)
{
    // align depth
    int u_depth = tree.at(u).depth;
    int v_depth = tree.at(v).depth;
    int diff = 0;
    if (u_depth < v_depth)
    {
        diff = v_depth - u_depth;
        v = get_nth_ancestor(tree, v, diff);
        v_depth = tree.at(v).depth;
    }
    else if (v_depth < u_depth)
    {
        diff = u_depth - v_depth;
        u = get_nth_ancestor(tree, u, diff);
        u_depth = tree.at(u).depth;
    }

    assert(v_depth == u_depth);
    assert(tree.at(u).pow2_ancestor.size() == tree.at(v).pow2_ancestor.size());

    if (u == v)
    {
        return u;
    }

    // move to ancestor if they have different ones
    int max_idx = (int)tree.at(u).pow2_ancestor.size()-1;
    for (int i = max_idx; 0 <= i; i--)
    {
        if (tree.at(u).pow2_ancestor.size() <= i)
        {
            continue;
        }
        if (tree.at(u).pow2_ancestor.at(i) != tree.at(v).pow2_ancestor.at(i))
        {
            u = tree.at(u).pow2_ancestor.at(i);
            v = tree.at(v).pow2_ancestor.at(i);
        }
    }
    return tree.at(u).pow2_ancestor.at(0);
}

int main()
{
    int nnode;
    scanf("%d", &nnode);
    vector<Node> tree(nnode);
    for (int i = 0; i < nnode; i++)
    {
        int nchild;
        scanf("%d", &nchild);
        for (int c = 0; c < nchild; c++)
        {
            int child_id;
            scanf("%d", &child_id);
            tree.at(i).child.push_back(child_id);
        }
    }
    record_ancestor(tree);

    int nquery;
    scanf("%d", &nquery);
    for (int i = 0; i < nquery; i++)
    {
        int u, v;
        scanf("%d %d", &u, &v);
        int result = compute_lca(tree, u, v);
        printf("%d\n", result);
    }

    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 27832
final_rank: 3

code_snippet: #include<bits/stdc++.h>
//#include <atcoder/all>
using namespace std;
//using namespace atcoder;

#define all(v) v.begin(),v.end()
using ll = long long;
using ull = unsigned long long;
using lll = __int128;
using vll=vector<ll>;
using vvll = vector<vector<ll>>;
using P = pair<ll,ll>;
using vp=vector<pair<ll, ll>>;
//using mint=modint1000000007;
//using mint=modint998244353;

const ll INF=1ll<<60;
ll mod10=1e9+7;
ll mod99=998244353;
const double PI = acos(-1);

#define rep(i,n) for (ll i=0;i<n;++i)
#define per(i,n) for(ll i=n-1;i>=0;--i)
#define rep2(i,a,n) for (ll i=a;i<n;++i)
#define per2(i,a,n) for (ll i=a;i>=n;--i)

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

struct LCA {
    vector<vector<int>> par;
    vector<int> dep;

    void dfs(const vector<vector<long long>>& ab,int now,int pre){
        par[0][now]=pre;dep[now]=(pre==-1?0:dep[pre]+1);
        for(int nxt:ab[now]) if(nxt!=pre) dfs(ab,nxt,now);
    }

    LCA(const vector<vector<long long>>& ab,int root =0){
        int N=ab.size(),k=1;
        while((1<<k)<N) k++;
        par=vector<vector<int>>(k,vector<int>(N,-1)),dep=vector<int>(N);
        dfs(ab,0,-1);
		for(int i=0;i<k-1;i++) for(int j=0;j<N;j++) par[i+1][j]=(par[i][j]==-1?-1:par[i][par[i][j]]);
    }

    int lca(int u,int v){
        if(dep[u]<dep[v]) swap(u,v);
        int k=par.size();
        for(int i=0;i<k;i++) if((dep[u]-dep[v])>>i&1) u=par[i][u];
        if(u==v) return u;
        for(int i=k-1;i>=0;i--) if(par[i][u]!=par[i][v]) u=par[i][u],v=par[i][v];
        return par[0][u];
    }

    int dist(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}

    bool is_on_path(int u,int v,int x){return dist(u,x)+dist(x,v)==dist(u,v);}

    bool cilmb(int u,int d){
        int k=par.size();
        for(int i=k-1;i>=0;i--) if(d>>i&1) u=par[i][u];
        return u;
    }


};

bool solve(){
	ll N;cin>>N;
	vvll ab(N);
	rep(i,N){
		ll k;cin>>k;
		rep(j,k){
			ll c;cin>>c;
			ab[i].push_back(c);
			ab[c].push_back(i);
		}
	}
	LCA lc(ab,0);
	ll q;cin>>q;
	
	while(q--){
		ll a,b;cin>>a>>b;
		cout<<lc.lca(a,b)<<endl;
	}
	return 0;
}




int main(){
	cin.tie(0);
	ios::sync_with_stdio(false);
	ll T=1;//cin>>T;
	rep(i,T) solve();
}
accuracy: 1
time_ms: 110
memory_kb: 21548
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;


int timer=0;
vector<int>tin,tout;
vector<vector<int>>graph,anc;
int l=21;

void build(vector<vector<int>>&graph,int node, int par){
    anc[node][0]=par;
    tin[node]=timer;
    timer++;
    for(int i=1;i<l;i++){
        anc[node][i]=anc[anc[node][i-1]][i-1];
    }        
    for(auto child:graph[node]){
        if(child!=par){
            build(graph,child,node);
        }
    }
    tout[node]=timer;
    timer++;
}
bool is_anc(int b, int a){
    if(tin[a]>=tin[b] and tout[a]<=tout[b]){
        return true;
    }return false;
}
int lca(int u, int v){
    if (is_anc(u, v)) return u;
    if (is_anc(v, u)) return v;
    for (int i = l - 1; i >= 0; i--) {
        if (!is_anc(anc[u][i], v)) {
            u = anc[u][i];
        }
    }
    return anc[u][0];
}
int main(){
    int n;
    cin>>n;
    vector<vector<int>>graph(n);
    for(int i=0;i<n;i++){
        int len;
        cin>>len;
        for(int j=0;j<len;j++){
            int child;cin>>child;
            graph[child].push_back(i);
            graph[i].push_back(child);
        }
    }
    timer=0;
    int root=0;
    tin.resize(n);
    tout.resize(n);
    anc.resize(n,vector<int>(l,0));
    build(graph,root,root);
    int q;
    cin>>q;
    while(q--){
        int u,v;
        cin>>u>>v;
        cout<<lca(u,v)<<endl;
    }

}
accuracy: 1
time_ms: 160
memory_kb: 27336
final_rank: 16