#include <bits/stdc++.h>
using namespace std;
template <class T1, class T2, int SIZE>
struct Array {
  T1 a[SIZE];
  T1& operator[](T2 x) { return a[x]; }
};
struct splay_index {
  int v;
  splay_index() = default;
  splay_index operator=(int x) {
    v = x;
    return *this;
  }
  splay_index operator+=(int x) {
    v += x;
    return *this;
  }
  splay_index operator-=(int x) {
    v -= x;
    return *this;
  }
  splay_index operator*=(int x) {
    v *= x;
    return *this;
  }
  splay_index operator/=(int x) {
    v /= x;
    return *this;
  }
  splay_index operator+(int x) {
    auto N = *this;
    N += x;
    return N;
  }
  splay_index operator-(int x) {
    auto N = *this;
    N -= x;
    return N;
  }
  splay_index operator*(int x) {
    auto N = *this;
    N *= x;
    return N;
  }
  splay_index operator/(int x) {
    auto N = *this;
    N /= x;
    return N;
  }
  splay_index& operator++() {
    v++;
    return *this;
  }
  operator int() { return v; }
};
struct splay_rank {
  int v;
  splay_rank() = default;
  splay_rank operator=(int x) {
    v = x;
    return *this;
  }
  splay_rank operator+=(int x) {
    v += x;
    return *this;
  }
  splay_rank operator-=(int x) {
    v -= x;
    return *this;
  }
  splay_rank operator*=(int x) {
    v *= x;
    return *this;
  }
  splay_rank operator/=(int x) {
    v /= x;
    return *this;
  }
  splay_rank operator+(int x) {
    auto N = *this;
    N += x;
    return N;
  }
  splay_rank operator-(int x) {
    auto N = *this;
    N -= x;
    return N;
  }
  splay_rank operator*(int x) {
    auto N = *this;
    N *= x;
    return N;
  }
  splay_rank operator/(int x) {
    auto N = *this;
    N /= x;
    return N;
  }
  operator int() { return v; }
};
struct tree_index {
  int v;
  tree_index() = default;
  tree_index operator=(int x) {
    v = x;
    return *this;
  }
  tree_index operator+=(int x) {
    v += x;
    return *this;
  }
  tree_index operator-=(int x) {
    v -= x;
    return *this;
  }
  tree_index operator*=(int x) {
    v *= x;
    return *this;
  }
  tree_index operator/=(int x) {
    v /= x;
    return *this;
  }
  tree_index operator+(int x) {
    auto N = *this;
    N += x;
    return N;
  }
  tree_index operator-(int x) {
    auto N = *this;
    N -= x;
    return N;
  }
  tree_index operator*(int x) {
    auto N = *this;
    N *= x;
    return N;
  }
  tree_index operator/(int x) {
    auto N = *this;
    N /= x;
    return N;
  }
  tree_index& operator++() {
    v++;
    return *this;
  }
  operator int() { return v; }
};
const int Maxn = 4 * 100005;
splay_index son[Maxn][2], f[Maxn];
Array<int, splay_index, Maxn> Size;
Array<splay_index, tree_index, Maxn> pos;
Array<tree_index, splay_index, Maxn> mark;
splay_index rt;
int n, m;
Array<int, splay_index, Maxn> Min, Max, dep, tag;
vector<tree_index> edge[Maxn];
void PushUp(splay_index x) {
  Min[x] = Max[x] = dep[x];
  Max[x] = max(Max[x], Max[son[x][0]]);
  Max[x] = max(Max[x], Max[son[x][1]]);
  Min[x] = min(Min[x], Min[son[x][0]]);
  Min[x] = min(Min[x], Min[son[x][1]]);
  Size[x] = 1 + Size[son[x][0]] + Size[son[x][1]];
}
void Add(splay_index x, int add) {
  if (!x) return;
  Max[x] += add;
  Min[x] += add;
  dep[x] += add;
  tag[x] += add;
}
void PushDown(splay_index x) {
  if (tag[x]) {
    Add(son[x][0], tag[x]);
    Add(son[x][1], tag[x]);
    tag[x] = 0;
  }
}
void PushDown(splay_index x, int) {
  if (f[x]) PushDown(f[x], 1);
  PushDown(x);
}
void Rotate(splay_index x) {
  splay_index fa = f[x], gr = f[fa];
  int s = son[fa][1] == x;
  splay_index sn = son[x][!s];
  son[f[x] = gr][son[gr][1] == fa] = x;
  son[f[fa] = x][!s] = fa;
  son[f[sn] = fa][s] = sn;
  PushUp(fa);
  PushUp(x);
}
void Splay(splay_index x, splay_index goal) {
  static splay_index Stack[Maxn];
  int top;
  Stack[top = 1] = x;
  for (splay_index i = x; f[i] != goal; i = f[i]) Stack[++top] = f[i];
  while (top) PushDown(Stack[top--]);
  while (f[x] != goal) {
    if (f[f[x]] != goal && (son[f[f[x]]][1] == f[x]) == (son[f[x]][1] == x))
      Rotate(f[x]);
    Rotate(x);
  }
  if (!goal) rt = x;
}
splay_index Select(splay_rank x) {
  splay_index p = rt;
  while (Size[son[p][0]] + 1 != x) {
    PushDown(p);
    if (Size[son[p][0]] < x)
      x -= Size[son[p][0]] + 1, p = son[p][1];
    else
      p = son[p][0];
  }
  return p;
}
splay_index Nxt(splay_index u) {
  Splay(u, splay_index{0});
  u = son[u][1];
  while (son[u][0]) PushDown(u), u = son[u][0];
  if (u > 2 * n - 1) return splay_index{0};
  return u;
}
splay_index Pre(splay_index u) {
  Splay(u, splay_index{0});
  u = son[u][0];
  while (son[u][1]) u = son[u][1];
  if (u > 2 * n - 1) return splay_index{0};
  return u;
}
void Split(splay_rank l, splay_rank r) {
  splay_index u = Select(l), v = Select(r + 2);
  Splay(u, splay_index{0});
  Splay(v, u);
}
void dfs(tree_index x, int d, tree_index fa) {
  static splay_index cnt;
  dep[++cnt] = d;
  pos[x] = cnt;
  mark[cnt] = x;
  for (tree_index y : edge[x]) {
    dfs(y, d + 1, x);
    dep[++cnt] = d;
    mark[cnt] = x;
  }
}
splay_index Build(splay_rank l, splay_rank r) {
  if (l > r) return splay_index{0};
  splay_index mid = splay_index{(l + r) / 2};
  Size[mid] = 1;
  Min[mid] = Max[mid] = dep[mid];
  f[son[mid][0] = Build(l, splay_rank{mid - 1})] = mid;
  f[son[mid][1] = Build(splay_rank{mid + 1}, r)] = mid;
  PushUp(mid);
  return splay_index{mid};
}
splay_index Ask(splay_rank l, splay_rank r, int op, int k) {
  Split(l, r);
  splay_index p = son[son[rt][1]][0], Ans = splay_index{0};
  while (p) {
    PushDown(p);
    if (dep[p] == k) Ans = p;
    if (Max[son[p][op]] >= k && k >= Min[son[p][op]])
      p = son[p][op];
    else if (dep[p] != k)
      p = son[p][!op];
    else
      break;
  }
  return Ans;
}
int Dep(tree_index u) {
  PushDown(pos[u], 1);
  return dep[pos[u]];
}
int Dep_splay_index(splay_index u) {
  PushDown(u, 1);
  return dep[u];
}
splay_rank Rank(splay_index u) {
  Splay(u, splay_index{0});
  return splay_rank{Size[son[u][0]]};
}
splay_index Askl(tree_index u) { return pos[u]; }
splay_index NewAsk(splay_rank l, splay_rank r, int dep) {
  Split(r, r);
  PushDown(rt);
  PushDown(son[rt][1]);
  PushDown(son[son[rt][1]][0]);
  assert(::dep[son[son[rt][1]][0]] == 1);
  Split(l, r);
  splay_index p = son[son[rt][1]][0], Ans = splay_index{0};
  while (p) {
    PushDown(p);
    if (::dep[p] < dep) {
      Ans = p;
      if (Min[son[p][0]] < dep) {
        p = son[p][0];
        continue;
      } else
        break;
    }
    if (Min[son[p][0]] < dep)
      p = son[p][0];
    else
      p = son[p][1];
  }
  if (Ans) return Pre(Ans);
  Split(r, r);
  return son[son[rt][1]][0];
}
splay_index Askr(tree_index u) {
  splay_index t = Nxt(pos[u]);
  if (!t || Dep(u) > Dep_splay_index(t)) {
    return pos[u];
  }
  return NewAsk(Rank(pos[u]) + 1, splay_rank{2 * n - 1}, Dep(u));
}
splay_index AskMin(splay_rank l, splay_rank r) {
  if (l > r) swap(l, r);
  Split(l, r);
  splay_index p = son[son[rt][1]][0];
  int Ans = Min[p];
  while (p) {
    PushDown(p);
    if (dep[p] == Ans) return p;
    if (Min[son[p][0]] == Ans)
      p = son[p][0];
    else
      p = son[p][1];
  }
}
tree_index LCA(tree_index u, tree_index v) {
  splay_index ul = Askl(u), vl = Askl(v);
  splay_index pos = AskMin(Rank(ul), Rank(vl));
  return mark[pos];
}
int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) {
    int x;
    scanf("%d", &x);
    edge[i].resize(x);
    for (tree_index& x : edge[i]) scanf("%d", &x);
  }
  dfs(tree_index{1}, 1, tree_index{0});
  splay_index RT = splay_index{2 * n}, RS = splay_index{2 * n + 1},
              nul = splay_index{0};
  Min[nul] = 1 << 30;
  Max[nul] = 0;
  rt = Build(splay_rank{1}, splay_rank{2 * n - 1});
  Size[RT] = 1;
  Min[RT] = 1 << 30;
  Max[RT] = 0;
  Size[RS] = 1;
  Min[RS] = 1 << 30;
  Max[RS] = 0;
  son[f[RS] = RT][1] = RS;
  son[f[rt] = RS][0] = rt;
  PushUp(RS);
  PushUp(RT);
  rt = RT;
  while (m--) {
    int op;
    scanf("%d", &op);
    if (op == 1) {
      tree_index u, v;
      scanf("%d%d", &u, &v);
      cout << Dep(u) + Dep(v) - 2 * Dep(LCA(u, v)) << "\n";
    }
    if (op == 2) {
      tree_index u;
      int k;
      scanf("%d%d", &u, &k);
      if (k == 1) exit(0);
      int depu = Dep(u);
      int depf = depu - 1;
      tree_index f = mark[Ask(splay_rank{1}, Rank(Askl(u)), 1, depf)];
      int depg = depu - k;
      tree_index g = mark[Ask(splay_rank{1}, Rank(Askl(u)), 1, depg)];
      splay_index oper_r = Askr(g);
      Split(Rank(Askl(u)), Rank(Askr(u)) + 1);
      PushDown(rt);
      PushDown(son[rt][1]);
      PushDown(son[son[rt][1]][0]);
      splay_index G = son[son[rt][1]][0];
      son[son[rt][1]][0] = 0;
      ::f[G] = 0;
      PushUp(son[rt][1]);
      PushUp(rt);
      int bak = rt;
      rt = G;
      Split(splay_rank{Size[G] - 1}, splay_rank{Size[G] - 2});
      PushDown(rt);
      PushDown(son[rt][1]);
      splay_index W = son[rt][1];
      son[rt][1] = 0;
      ::f[W] = 0;
      PushUp(rt);
      mark[W] = g;
      son[::f[rt] = W][1] = rt;
      rt = W;
      PushUp(rt);
      G = rt;
      rt = bak;
      Split(Rank(oper_r), Rank(oper_r) - 1);
      son[son[rt][1]][0] = G;
      ::f[son[son[rt][1]][0]] = son[rt][1];
      Add(son[son[rt][1]][0], -k + 1);
      PushUp(son[rt][1]);
      PushUp(rt);
    }
    if (op == 3) {
      int k;
      scanf("%d", &k);
      k++;
      cout << mark[Ask(splay_rank{1}, splay_rank{2 * n - 1}, 1, k)] << "\n";
    }
  }
  return 0;
}
