#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3fffffff;
const int SINF = 0x7fffffff;
const long long LINF = 0x3fffffffffffffff;
const long long SLINF = 0x7fffffffffffffff;
const int MINF = 0x3f3f3f3f;
const int MAXN = 200007;
int rnd() { return rand() * RAND_MAX + rand(); }
class treapT {
 public:
  void init() {
    for (int i = 0; i < MAXN; ++i) w[i] = rnd();
    memset(mini, MINF, sizeof(mini));
  }
  void upd(int now, int x) {
    tag[now] += x;
    nd[now] += x;
    push_down(now);
    upd(now);
  }
  int getlast(int now) {
    push_down(now);
    while (ch[now][1]) now = ch[now][1], push_down(now);
    return now;
  }
  int querylast(int now, int k) {
    push_down(now);
    while (maxi[now] >= k) {
      if (maxi[ch[now][1]] >= k)
        now = ch[now][1];
      else if (nd[now] == k)
        return now;
      else
        now = ch[now][0];
      push_down(now);
    }
    return -1;
  }
  int query(int now) {
    rev_push(now);
    return nd[now];
  }
  int querymin(int now) { return mini[now]; }
  int queryfa(int now, int k) {
    rev_push(now);
    int pre = ch[now][1];
    while ((ch[now][0] == pre) || (mini[ch[now][0]] > k && nd[now] > k))
      pre = now, now = fa[now];
    if (ch[now][1] == pre && nd[now] <= k) return now;
    now = ch[now][0];
    push_down(now);
    while (mini[now] <= k) {
      if (mini[ch[now][1]] <= k)
        now = ch[now][1];
      else if (nd[now] == k)
        return now;
      else
        now = ch[now][0];
      push_down(now);
    }
  }
  int querynxt(int now, int k) {
    rev_push(now);
    int pre = ch[now][0];
    while ((ch[now][1] == pre) || (mini[ch[now][1]] > k && nd[now] > k))
      pre = now, now = fa[now];
    if (ch[now][0] == pre && nd[now] <= k) return now;
    now = ch[now][1];
    push_down(now);
    while (mini[now] <= k) {
      if (mini[ch[now][0]] <= k)
        now = ch[now][0];
      else if (nd[now] == k)
        return now;
      else
        now = ch[now][1];
      push_down(now);
    }
  }
  int merge(int nl, int nr) {
    if (!nl) return nr;
    if (!nr) return nl;
    push_down(nl), push_down(nr);
    if (w[nl] < w[nr]) {
      fa[ch[nl][1] = merge(ch[nl][1], nr)] = nl;
      upd(nl);
      return nl;
    } else {
      fa[ch[nr][0] = merge(nl, ch[nr][0])] = nr;
      upd(nr);
      return nr;
    }
  }
  pair<int, int> split(int now, bool flag) {
    rev_push(now);
    pair<int, int> ans;
    if (flag) {
      ans = make_pair(now, ch[now][1]);
      fa[ch[now][1]] = 0, ch[now][1] = 0;
    } else {
      ans = make_pair(ch[now][0], now);
      fa[ch[now][0]] = 0, ch[now][0] = 0;
    }
    upd(now);
    int nf;
    for (; fa[now]; now = nf) {
      nf = fa[now];
      if (ch[nf][0] == now) {
        fa[ch[nf][0]] = 0, ch[nf][0] = 0;
        fa[ch[nf][0] = ans.second] = nf;
        ans.second = nf;
      } else {
        fa[ch[nf][1]] = 0, ch[nf][1] = 0;
        fa[ch[nf][1] = ans.first] = nf;
        ans.first = nf;
      }
      upd(nf);
    }
    return ans;
  }
  void print(int now) {
    if (!now) return;
    print(ch[now][0]);
    fprintf(stderr, "(%d,%d,%d,%d,%d,(%d,%d),%d) ", now, nd[now], tag[now],
            mini[now], maxi[now], ch[now][0], ch[now][1], fa[now]);
    fflush(stderr);
    print(ch[now][1]);
  }

 private:
  void push_down(int now) {
    if (tag[now]) {
      if (ch[now][0])
        tag[ch[now][0]] += tag[now], nd[ch[now][0]] += tag[now],
            mini[ch[now][0]] += tag[now], maxi[ch[now][0]] += tag[now];
      if (ch[now][1])
        tag[ch[now][1]] += tag[now], nd[ch[now][1]] += tag[now],
            mini[ch[now][1]] += tag[now], maxi[ch[now][1]] += tag[now];
      tag[now] = 0;
    }
  }
  void rev_push(int now) {
    if (fa[now]) rev_push(fa[now]);
    push_down(now);
  }
  void upd(int now) {
    mini[now] =
        min((((mini[ch[now][0]]) < (mini[ch[now][1]])) ? (mini[ch[now][0]])
                                                       : (mini[ch[now][1]])),
            nd[now]);
    maxi[now] =
        max((((maxi[ch[now][0]]) > (maxi[ch[now][1]])) ? (maxi[ch[now][0]])
                                                       : (maxi[ch[now][1]])),
            nd[now]);
  }
  int fa[MAXN], ch[MAXN][2], w[MAXN];
  int mini[MAXN], maxi[MAXN], nd[MAXN], tag[MAXN];
} trp;
int n, q, rt;
vector<int> e[MAXN];
int qtp, qa, qb;
int il[MAXN], ir[MAXN], id[MAXN], kc;
int lev[MAXN];
void init();
void input();
void inputq();
void work();
void dfs(int now);
int query1(int u, int v);
void query2(int u, int h);
int query3(int k);
int main() {
  init();
  input();
  work();
}
void init() {
  ios::sync_with_stdio(false);
  srand(1357924680);
  trp.init();
}
void input() {
  scanf("%d%d", &n, &q);
  int kk, x;
  for (int i = 1; i <= n; ++i) {
    scanf("%d", &kk);
    for (int j = 0; j < kk; ++j) {
      scanf("%d", &x);
      e[i].push_back(x);
    }
  }
}
void inputq() {
  scanf("%d%d", &qtp, &qa);
  if (qtp <= 2) scanf("%d", &qb);
}
void work() {
  lev[1] = 1;
  dfs(1);
  for (int i = 1; i <= kc; ++i) trp.upd(i, lev[id[i]]);
  rt = 1;
  for (int i = 2; i <= kc; ++i) rt = trp.merge(rt, i);
  for (int _q = 0; _q < q; ++_q) {
    inputq();
    if (qtp == 1)
      printf("%d\n", query1(qa, qb));
    else if (qtp == 2)
      query2(qa, qb);
    else
      printf("%d\n", query3(qa));
  }
}
void dfs(int now) {
  il[now] = ir[now] = ++kc;
  id[kc] = now;
  for (auto v : e[now]) {
    lev[v] = lev[now] + 1;
    dfs(v);
    ir[now] = ++kc;
    id[kc] = now;
  }
}
int query1(int u, int v) {
  if (u == v) return 0;
  pair<int, int> ret1 = trp.split(il[u], true);
  pair<int, int> ret2 = trp.split(il[v], true);
  int l, mid, r;
  if (ret1.first == ret2.first || ret1.first == ret2.second)
    l = ret2.first, mid = ret2.second, r = ret1.second;
  else
    l = ret1.first, mid = ret2.first, r = ret2.second;
  int lcal = trp.querymin(mid);
  rt = trp.merge(trp.merge(l, mid), r);
  int lu = trp.query(il[u]), lv = trp.query(il[v]);
  lcal = min(lcal, (((lu) < (lv)) ? (lu) : (lv)));
  return lu + lv - lcal * 2;
}
void query2(int u, int h) {
  int nl = trp.query(il[u]);
  int tl = nl - h;
  int pl = trp.queryfa(il[u], nl - 1);
  int nf = id[pl];
  int fat = trp.querynxt(ir[u], nl - 1);
  int vf = id[trp.queryfa(il[u], tl)];
  pair<int, int> ret1 = trp.split(il[u], false), ret2 = trp.split(fat, true);
  int mid = ret2.first;
  trp.upd(mid, -(h - 1));
  trp.merge(ret1.first, ret2.second);
  ret1 = trp.split(ir[vf], true);
  rt = trp.merge(trp.merge(ret1.first, mid), ret1.second);
  id[fat] = vf, ir[vf] = fat;
  if (ir[nf] == fat) ir[nf] = pl;
}
int query3(int k) { return id[trp.querylast(rt, k + 1)]; }
