#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
const int MAXN = 1e6 + 10;
const int INF = 1e9;
struct LCT {
  struct node {
    int fa, ch[2];
    int reverse;
    bool is_root;
    int sz;
    int vsz;
    long long val;
    int id;
    long long minv;
  } Tree[MAXN];
  int n;
  void init(int MN) {
    n = MN;
    for (int i = 1; i <= MN; i++) {
      Tree[i].reverse = Tree[i].fa = Tree[i].ch[0] = Tree[i].ch[1] = 0;
      Tree[i].is_root = true;
      Tree[i].val = INF;
      Tree[i].sz = 1;
      Tree[i].id = i;
    }
  }
  bool getson(int x) { return x == Tree[Tree[x].fa].ch[1]; }
  bool isroot(int x) {
    return Tree[Tree[x].fa].ch[0] != x && Tree[Tree[x].fa].ch[1] != x;
  }
  void pushreverse(int x) {
    if (!x) return;
    swap(Tree[x].ch[0], Tree[x].ch[1]);
    Tree[x].reverse ^= 1;
  }
  void pushdown(int x) {
    if (Tree[x].reverse) {
      pushreverse(Tree[x].ch[0]);
      pushreverse(Tree[x].ch[1]);
      Tree[x].reverse = 0;
    }
  }
  void update(int x) {
    int l = Tree[x].ch[0], r = Tree[x].ch[1];
    Tree[x].sz = 1 + Tree[x].vsz;
    Tree[x].minv = Tree[x].val;
    Tree[x].id = x;
    if (l) {
      Tree[x].sz += Tree[l].sz;
      if (Tree[l].minv < Tree[x].minv) {
        Tree[x].minv = Tree[l].minv;
        Tree[x].id = Tree[l].id;
      }
    }
    if (r) {
      Tree[x].sz += Tree[r].sz;
      if (Tree[r].minv < Tree[x].minv) {
        Tree[x].minv = Tree[r].minv;
        Tree[x].id = Tree[r].id;
      }
    }
  }
  void rotate(int x) {
    if (Tree[x].is_root) return;
    int k = getson(x), fa = Tree[x].fa;
    int fafa = Tree[fa].fa;
    pushdown(fa);
    pushdown(x);
    Tree[fa].ch[k] = Tree[x].ch[k ^ 1];
    if (Tree[x].ch[k ^ 1]) Tree[Tree[x].ch[k ^ 1]].fa = fa;
    Tree[x].ch[k ^ 1] = fa;
    Tree[fa].fa = x;
    Tree[x].fa = fafa;
    if (!Tree[fa].is_root)
      Tree[fafa].ch[fa == Tree[fafa].ch[1]] = x;
    else
      Tree[x].is_root = true, Tree[fa].is_root = false;
    update(fa);
    update(x);
  }
  void push(int x) {
    if (!Tree[x].is_root) push(Tree[x].fa);
    pushdown(x);
  }
  int findroot(int x) {
    access(x);
    Splay(x);
    pushdown(x);
    while (Tree[x].ch[0]) pushdown(x = Tree[x].ch[0]);
    return x;
  }
  void Splay(int x) {
    push(x);
    for (int fa; !Tree[x].is_root; rotate(x)) {
      if (!Tree[fa = Tree[x].fa].is_root) {
        rotate((getson(x) == getson(fa)) ? fa : x);
      }
    }
  }
  void access(int x) {
    int y = 0;
    do {
      Splay(x);
      Tree[Tree[x].ch[1]].is_root = true;
      Tree[x].vsz += Tree[Tree[x].ch[1]].sz;
      Tree[Tree[x].ch[1] = y].is_root = false;
      Tree[x].vsz -= Tree[Tree[x].ch[1]].sz;
      update(x);
      x = Tree[y = x].fa;
    } while (x);
  }
  void mroot(int x) {
    access(x);
    Splay(x);
    pushreverse(x);
  }
  void link(int u, int v) {
    mroot(u);
    if (findroot(v) != u) {
      Tree[u].fa = v;
      Tree[v].vsz += Tree[u].sz;
      update(v);
    }
  }
  void cut(int u, int v) {
    mroot(u);
    access(v);
    Splay(v);
    pushdown(v);
    if (Tree[v].ch[0]) {
      Tree[Tree[v].ch[0]].fa = Tree[v].fa;
      Tree[Tree[v].ch[0]].is_root = true;
    }
    Tree[v].fa = 0;
    Tree[v].ch[0] = 0;
    update(v);
  }
  bool judge(int u, int v) {
    while (Tree[u].fa) u = Tree[u].fa;
    while (Tree[v].fa) v = Tree[v].fa;
    return u == v;
  }
  void split(int u, int v) {
    mroot(u);
    access(v);
    Splay(v);
  }
  void modify(int x, int v) {
    access(x);
    Splay(x);
    Tree[x].val = v;
    update(x);
  }
} lct;
int getroot(int x) {
  long long lsum = 0, rsum = 0, lnow = 0, rnow = 0;
  int son = lct.Tree[x].sz / 2, flag = lct.Tree[x].sz & 1, root = lct.n + 1;
  while (x) {
    lct.pushdown(x);
    int l = lct.Tree[x].ch[0], r = lct.Tree[x].ch[1];
    lnow = lsum + lct.Tree[l].sz;
    rnow = rsum + lct.Tree[r].sz;
    if (lnow <= son && rnow <= son) {
      if (flag) {
        root = x;
        break;
      } else {
        root = min(root, x);
      }
    }
    if (lnow < rnow) {
      lsum += lct.Tree[l].sz + lct.Tree[x].vsz + 1;
      x = r;
    } else {
      rsum += lct.Tree[r].sz + lct.Tree[x].vsz + 1;
      x = l;
    }
  }
  lct.Splay(root);
  return root;
}
void debug() {
  for (int i = 1; i <= 5; i++) {
    lct.mroot(i);
    cout << "i sz:" << i << " " << lct.Tree[i].sz << endl;
  }
}
struct operation {
  int day;
  int id;
};
int u[MAXN];
int v[MAXN];
queue<operation> que;
int main() {
  int n, q, k;
  scanf("%d%d%d", &n, &q, &k);
  lct.n = n;
  lct.init(n + q);
  int now = 1;
  for (int i = 1; i <= q; i++) {
    while (!que.empty()) {
      auto cur = que.front();
      if (cur.day < now - k + 1) {
        lct.cut(u[cur.id], cur.id + n);
        lct.cut(v[cur.id], cur.id + n);
        que.pop();
      } else {
        break;
      }
    }
    int op;
    scanf("%d", &op);
    if (op == 1) {
      scanf("%d%d", &u[i], &v[i]);
      que.push({now, i});
      if (lct.judge(u[i], v[i])) {
        lct.split(u[i], v[i]);
        int num = lct.Tree[v[i]].id;
        lct.cut(num, u[num - n]);
        lct.cut(num, v[num - n]);
      }
      lct.Tree[i + n].val = now;
      lct.link(u[i], i + n);
      lct.link(v[i], i + n);
    } else if (op == 2) {
      int x;
      scanf("%d", &x);
      lct.mroot(x);
      printf("%d\n", lct.Tree[x].sz / 2 + 1);
    } else {
      now++;
    }
  }
  return 0;
}
