#include <bits/stdc++.h>
using namespace std;
namespace DynamicTree {
unsigned int randxor() {
  static unsigned int x = 123456789, y = 362436069, z = 521288629, w = 88675123;
  unsigned int t;
  t = (x ^ (x << 11));
  x = y;
  y = z;
  z = w;
  return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
}
struct D_NODE {
  int lazy_for_dep;
  int dep;
  D_NODE *pr;
  D_NODE *pair_of_end;
  int __PRIORITY;
  int size;
  long long int lazy_for_dist;
  long long int dist;
  long long int have_dist;
  D_NODE *lef;
  D_NODE *rig;
  int idx;
  int MINT_DEP;
  D_NODE *MINT;
  int MAXT_DEP;
  D_NODE *MAXT;
  bool start_flag;
  int NUMBER_OF_START_FLAG;
  D_NODE() {
    NUMBER_OF_START_FLAG;
    start_flag = false;
    lazy_for_dep = 0;
    dep = 0;
    lazy_for_dist = 0;
    dist = 0;
    lef = rig = NULL;
    __PRIORITY = randxor();
    pair_of_end = NULL;
    MINT = NULL;
    MINT_DEP = INT_MAX;
    MAXT = NULL;
    MAXT_DEP = INT_MIN;
    pr = NULL;
    size = 0;
  }
};
const int MEMORY_SIZE = 2000000;
D_NODE dymmy[MEMORY_SIZE];
int ord = 0;
D_NODE *neww() {
  if (ord < MEMORY_SIZE) {
    ord++;
    return &dymmy[ord - 1];
  }
  return new D_NODE();
}
const int VECTOR_SIZE = 1000000;
D_NODE *VEC[VECTOR_SIZE];
int VECTOR_CUR_INDEX = 0;
inline void see(D_NODE *f, bool B = false) {
  if (f->lef) {
    see(f->lef, true);
  }
  cout << f->idx << " ";
  if (f->rig) {
    see(f->rig, true);
  }
  if (B == false) {
    puts("");
  }
}
int get_size(D_NODE *b) {
  if (b == NULL) {
    return 0;
  }
  return b->size;
}
void update(D_NODE *B) {
  if (B == NULL) {
    return;
  }
  if (B->lazy_for_dep) {
    B->dep += B->lazy_for_dep;
    if (B->lef) {
      B->lef->lazy_for_dep += B->lazy_for_dep;
    }
    if (B->rig) {
      B->rig->lazy_for_dep += B->lazy_for_dep;
    }
    B->lazy_for_dep = 0;
  }
  if (B->lazy_for_dist) {
    B->dist += B->lazy_for_dist;
    if (B->lef) {
      B->lef->lazy_for_dist += B->lazy_for_dist;
    }
    if (B->rig) {
      B->rig->lazy_for_dist += B->lazy_for_dist;
    }
    B->lazy_for_dist = 0;
  }
  B->size = 1 + get_size(B->lef) + get_size(B->rig);
  if (B->lef) {
    B->lef->pr = B;
  }
  if (B->rig) {
    B->rig->pr = B;
  }
  B->MINT = B;
  B->MINT_DEP = B->dep;
  if (B->lef && B->MINT_DEP > B->lef->MINT_DEP + B->lef->lazy_for_dep) {
    B->MINT_DEP = B->lef->MINT_DEP + B->lef->lazy_for_dep;
    B->MINT = B->lef->MINT;
  }
  if (B->rig && B->MINT_DEP > B->rig->MINT_DEP + B->rig->lazy_for_dep) {
    B->MINT_DEP = B->rig->MINT_DEP + B->rig->lazy_for_dep;
    B->MINT = B->rig->MINT;
  }
  B->MAXT = B;
  B->MAXT_DEP = B->dep;
  if (B->lef && B->MAXT_DEP < B->lef->MAXT_DEP + B->lef->lazy_for_dep) {
    B->MAXT_DEP = B->lef->MAXT_DEP + B->lef->lazy_for_dep;
    B->MAXT = B->lef->MAXT;
  }
  if (B->rig && B->MAXT_DEP < B->rig->MAXT_DEP + B->rig->lazy_for_dep) {
    B->MAXT_DEP = B->rig->MAXT_DEP + B->rig->lazy_for_dep;
    B->MAXT = B->rig->MAXT;
  }
  B->NUMBER_OF_START_FLAG = B->start_flag;
  if (B->lef) {
    B->NUMBER_OF_START_FLAG += B->lef->NUMBER_OF_START_FLAG;
  }
  if (B->rig) {
    B->NUMBER_OF_START_FLAG += B->rig->NUMBER_OF_START_FLAG;
  }
}
D_NODE *find_root(D_NODE *a) {
  while (a->pr != NULL) {
    update(a);
    a = a->pr;
  }
  return a;
}
void path_update(D_NODE *a, bool dep_only = false) {
  VECTOR_CUR_INDEX = 0;
  while (a != NULL) {
    VEC[VECTOR_CUR_INDEX] = a;
    VECTOR_CUR_INDEX++;
    a = a->pr;
  }
  int k = VECTOR_CUR_INDEX - 1;
  if (dep_only == false) {
    for (int i = k; i >= 0; i--) {
      update(VEC[i]);
    }
  }
  for (int i = 0; i <= k; i++) {
    update(VEC[i]);
  }
}
inline D_NODE *merge(D_NODE *a, D_NODE *b) {
  if (a != NULL) update(a);
  if (b != NULL) update(b);
  if (a == NULL) {
    path_update(b);
    return b;
  }
  if (b == NULL) {
    path_update(a);
    return a;
  }
  if (a->__PRIORITY > b->__PRIORITY) {
    a->rig = merge(a->rig, b);
    update(a);
    return a;
  } else {
    b->lef = merge(a, b->lef);
    update(b);
    return b;
  }
}
int calc_left_size(D_NODE *a) {
  if (a == NULL) {
    return 0;
  }
  path_update(a);
  int sum = 1 + get_size(a->lef);
  D_NODE *pre = a;
  a = a->pr;
  while (a != NULL) {
    if (a->rig == pre) {
      sum++;
      sum += get_size(a->lef);
    }
    pre = a;
    a = a->pr;
  }
  return sum;
}
inline void split_base_cnt(D_NODE *b, D_NODE *&l, D_NODE *&r, int x) {
  if (b == NULL) {
    D_NODE *L = NULL;
    if (l) L = l->pr;
    D_NODE *R = NULL;
    if (r) R = r->pr;
    l = r = NULL;
    path_update(L);
    path_update(R);
    return;
  }
  update(b);
  int __get_size = get_size(b->lef);
  if (__get_size >= x) {
    r = b;
    update(b->lef);
    split_base_cnt(b->lef, l, b->lef, x);
    return;
  }
  if (__get_size + 1 == x) {
    l = b;
    r = b->rig;
    l->rig = NULL;
    path_update(l);
    path_update(r);
    return;
  }
  l = b;
  update(b->rig);
  split_base_cnt(b->rig, b->rig, r, x - 1 - __get_size);
}
D_NODE *cut(D_NODE *&tree, D_NODE *root_of_new_subtree) {
  if (root_of_new_subtree == NULL) {
    cout << "error cut" << endl;
    exit(0);
    return NULL;
  }
  D_NODE *star = root_of_new_subtree;
  D_NODE *en = root_of_new_subtree->pair_of_end;
  int __LEFT_SIZE = calc_left_size(star);
  int cur_dep = root_of_new_subtree->dep;
  long long int cur_dist = root_of_new_subtree->dist;
  D_NODE *l1 = NULL;
  D_NODE *l2 = NULL;
  D_NODE *l3 = NULL;
  split_base_cnt(tree, l1, l2, __LEFT_SIZE - 1);
  if (l1) {
    l1->pr = NULL;
  }
  if (l2) {
    l2->pr = NULL;
  }
  if (tree) {
    tree->pr = NULL;
  }
  int __RIGHT_SIZE = calc_left_size(en);
  split_base_cnt(l2, l2, l3, __RIGHT_SIZE);
  if (l2) {
    l2->pr = NULL;
  }
  if (l3) {
    l3->pr = NULL;
  }
  tree = merge(l1, l3);
  if (tree) tree->pr = NULL;
  if (l2) l2->pr = NULL;
  if (l2) l2->lazy_for_dep += -cur_dep;
  if (l2) l2->lazy_for_dist += -cur_dist;
  update(l2);
  return l2;
}
void add_distance(D_NODE *&tree, D_NODE *ap, long long int add) {
  path_update(ap);
  ap->have_dist += add;
  D_NODE *star = ap;
  D_NODE *en = star->pair_of_end;
  int __LEFT_SIZE = calc_left_size(star);
  D_NODE *l1;
  D_NODE *l2;
  D_NODE *l3;
  split_base_cnt(tree, l1, l2, __LEFT_SIZE - 1);
  int __RIGHT_SIZE = calc_left_size(en);
  split_base_cnt(tree, l2, l3, __RIGHT_SIZE);
  l2->lazy_for_dist += add;
  update(l2);
  tree = merge(l1, l2);
  tree = merge(tree, l3);
  tree->pr = NULL;
  return;
}
void link(D_NODE *&tree, D_NODE *nex_parent, D_NODE *linked,
          long long int subtree_add_dist = 1) {
  if (linked == NULL) {
    return;
  }
  path_update(nex_parent);
  path_update(nex_parent->pair_of_end);
  D_NODE *ne = neww();
  (*ne) = (*nex_parent->pair_of_end);
  ne->pr = NULL;
  ne->lef = ne->rig = NULL;
  ne->size = 1;
  ne->MINT_DEP = ne->MAXT_DEP = ne->dep;
  ne->MINT = ne->MAXT = ne;
  update(ne);
  update(linked);
  linked->lazy_for_dist += nex_parent->dist + subtree_add_dist;
  linked->lazy_for_dep += nex_parent->dep + 1;
  update(linked);
  linked = merge(ne, linked);
  linked->pr = NULL;
  int LEFT_SIZE = calc_left_size(nex_parent->pair_of_end);
  LEFT_SIZE--;
  D_NODE *l1 = NULL;
  D_NODE *l2 = NULL;
  split_base_cnt(tree, l1, l2, LEFT_SIZE);
  if (l1) l1->pr = NULL;
  if (l2) l2->pr = NULL;
  tree = merge(l1, linked);
  if (tree) {
    tree->pr = NULL;
  }
  D_NODE *cp_ = tree;
  tree = merge(tree, l2);
  tree->pr = NULL;
}
D_NODE *LCA(D_NODE *&tree, D_NODE *a, D_NODE *b) {
  if (a == b) return a;
  tree->pr = NULL;
  int lef1 = calc_left_size(a);
  int rig1 = calc_left_size(a->pair_of_end);
  int lef2 = calc_left_size(b);
  int rig2 = calc_left_size(b->pair_of_end);
  if (lef1 <= lef2 && rig2 <= rig1) {
    return a;
  }
  if (lef2 <= lef1 && lef1 <= rig2) {
    return b;
  }
  int mint = lef1;
  if (lef1 > lef2) {
    mint = lef2;
  }
  int maxt = rig1;
  if (rig1 < rig2) {
    maxt = rig2;
  }
  D_NODE *L1;
  D_NODE *L2;
  D_NODE *L3;
  D_NODE *ANS;
  split_base_cnt(tree, L1, L2, mint - 1);
  split_base_cnt(L2, L2, L3, maxt - mint + 1);
  if (L1) {
    L1->pr = NULL;
  }
  if (L2) {
    L2->pr = NULL;
  }
  if (L3) {
    L3->pr = NULL;
  }
  ANS = L2->MINT;
  tree = merge(L1, L2);
  tree = merge(tree, L3);
  if (tree) tree->pr = NULL;
  return ANS;
}
long long int distance(D_NODE *&tree, D_NODE *a, D_NODE *b) {
  if (a == b) {
    return 0;
  }
  tree->pr = NULL;
  D_NODE *LC = LCA(tree, a, b);
  path_update(LC);
  long long int ans = a->dist + b->dist - 2LL * LC->dist;
  return ans;
}
void push_back(D_NODE *&tree, D_NODE *a) {
  tree = merge(tree, a);
  tree->pr = NULL;
}
D_NODE *go_up(D_NODE *tree, D_NODE *from, int x) {
  if (x == 0) {
    return from;
  }
  path_update(from);
  int want_dep = from->dep - x;
  update(from);
  update(from->lef);
  if (from->lef && from->lef->MINT_DEP <= want_dep &&
      want_dep <= from->lef->MAXT_DEP) {
    from = from->lef;
    while (1) {
      update(from);
      update(from->rig);
      if (from->rig && from->rig->MINT_DEP <= want_dep &&
          from->rig->MAXT_DEP >= want_dep) {
        from = from->rig;
        continue;
      }
      if (from->dep == want_dep) {
        return from;
      }
      from = from->lef;
      continue;
    }
  }
  D_NODE *pre = from;
  from = from->pr;
  while (1) {
    update(from);
    update(from->lef);
    if (from->lef == pre) {
      pre = from;
      from = from->pr;
      continue;
    }
    if (from->dep == want_dep) {
      return from;
    }
    if (from->lef && from->lef->MINT_DEP <= want_dep &&
        want_dep <= from->lef->MAXT_DEP) {
      from = from->lef;
      while (1) {
        update(from);
        update(from->rig);
        if (from->rig && from->rig->MINT_DEP <= want_dep &&
            from->rig->MAXT_DEP >= want_dep) {
          from = from->rig;
          continue;
        }
        if (from->dep == want_dep) {
          return from;
        }
        from = from->lef;
        continue;
      }
    } else {
      pre = from;
      from = from->pr;
    }
  }
}
D_NODE *F(D_NODE *tree, int k) {
  D_NODE *a = tree;
  update(a);
  while (1) {
    update(a);
    if (a->rig) {
      update(a->rig);
      if (a->rig->MAXT_DEP >= k && a->rig->MINT_DEP <= k) {
        a = a->rig;
        continue;
      }
    }
    if (a->dep == k) {
      return a;
    }
    a = a->lef;
  }
  return a;
}
inline void se(D_NODE *f, bool B = false) {
  update(f);
  int MX = f->MAXT_DEP;
  int MN = f->MINT_DEP;
  if (f->lef) {
    se(f->lef, true);
  }
  if (f->rig) {
    se(f->rig, true);
  }
  update(f);
}
}  // namespace DynamicTree
DynamicTree::D_NODE *root;
int n;
int m;
vector<int> v[100002];
DynamicTree::D_NODE *linkk[100002];
int dep[100002];
int star[100002];
int en[100002];
bool use[100002];
vector<int> vv;
int siz;
inline void dfs(int b, int h = 0) {
  use[b] = true;
  dep[b] = h;
  star[b] = siz;
  siz++;
  vv.push_back(b);
  for (int i = 0; i < v[b].size(); i++) {
    if (use[v[b][i]]) {
      continue;
    }
    dfs(v[b][i], h + 1);
    en[b] = siz;
    siz++;
    vv.push_back(b);
  }
  if (siz - 1 == star[b]) {
    vv.push_back(b);
    en[b] = siz;
    siz++;
  }
}
void make() {
  dfs(0);
  for (int i = 0; i < vv.size(); i++) {
    DynamicTree::D_NODE *add = DynamicTree::neww();
    add->dep = dep[vv[i]];
    add->idx = vv[i];
    add->MINT = add;
    add->MINT_DEP = dep[vv[i]];
    add->MAXT_DEP = dep[vv[i]];
    add->MAXT = add;
    add->dist = dep[vv[i]];
    add->have_dist = 1;
    add->size = 1;
    if (star[vv[i]] == i) {
      add->start_flag = true;
      add->NUMBER_OF_START_FLAG = 1;
      linkk[vv[i]] = add;
      DynamicTree::push_back(root, add);
    } else {
      linkk[vv[i]]->pair_of_end = add;
      DynamicTree::push_back(root, add);
    }
  }
}
int main() {
  scanf("%d%d", &n, &m);
  for (int i = 0; i < n; i++) {
    int k;
    scanf("%d", &k);
    for (int j = 0; j < k; j++) {
      int a;
      scanf("%d", &a);
      a--;
      v[i].push_back(a);
    }
  }
  make();
  int cc = 0;
  while (m--) {
    int ty;
    scanf("%d", &ty);
    if (ty == 1) {
      cc++;
      int a, b;
      scanf("%d%d", &a, &b);
      a--;
      b--;
      DynamicTree::calc_left_size(linkk[a]);
      DynamicTree::calc_left_size(linkk[b]);
      printf("%lld\n", DynamicTree::distance(root, linkk[a], linkk[b]));
      continue;
    }
    if (ty == 2) {
      int a, b;
      scanf("%d%d", &a, &b);
      a--;
      b = DynamicTree::go_up(root, linkk[a], b)->idx;
      DynamicTree::D_NODE *nw = DynamicTree::cut(root, linkk[a]);
      int ne = linkk[b]->dep + 1;
      update(nw);
      update(root);
      if (root) root->pr = NULL;
      if (nw) nw->pr = NULL;
      DynamicTree::link(root, linkk[b], nw);
      DynamicTree::update(root);
      root->pr = NULL;
      continue;
    }
    int k;
    scanf("%d", &k);
    cc++;
    DynamicTree::D_NODE *ans = DynamicTree::F(root, k);
    printf("%d\n", ans->idx + 1);
  }
  return 0;
}
