#include <bits/stdc++.h>
struct MI {
 private:
  char bb[4096];
  FILE* f;
  char *bs, *be;
  char e;
  bool o, l;

 public:
  MI() : f(stdin) {}
  inline char get() {
    if (o) {
      o = 0;
      return e;
    }
    if (bs == be) be = (bs = bb) + fread(bb, 1, sizeof(bb), f);
    if (bs == be) {
      l = 1;
      return -1;
    };
    return *bs++;
  }
  inline void unget(char c) {
    o = 1;
    e = c;
  }
  template <class T>
  inline T read() {
    T r;
    *this > r;
    return r;
  }
  template <class T>
  inline MI& operator>(T&);
};
template <class T>
struct Q {
  const static bool U = T(-1) >= T(0);
  inline void operator()(MI& t, T& r) const {
    r = 0;
    char c;
    bool y = 0;
    if (U)
      for (;;) {
        c = t.get();
        if (c == -1) goto E;
        if (isdigit(c)) break;
      }
    else
      for (;;) {
        c = t.get();
        if (c == -1) goto E;
        if (c == '-') {
          c = t.get();
          if (isdigit(c)) {
            y = 1;
            break;
          };
        } else if (isdigit(c))
          break;
        ;
      };
    for (;;) {
      if (c == -1) goto E;
      if (isdigit(c))
        r = r * 10 + (c ^ 48);
      else
        break;
      c = t.get();
    }
    t.unget(c);
  E:;
    if (y) r = -r;
  }
};
template <>
struct Q<char> {};
template <class T>
inline MI& MI::operator>(T& t) {
  Q<T>()(*this, t);
  return *this;
}
template <class T>
inline std::ostream& operator<(std::ostream& out, const T& t) {
  return out << t;
}
using std::cout;
MI cin;
using std::make_pair;
using std::pair;
using std::swap;
using std::vector;
const int nmax = 100001;
const int mmax = 500001;
const int tmax = 1048577;
int n, m, K;
int ope[mmax][3];
int que[mmax];
vector<pair<int, int> > sta;
int f[nmax], dis[nmax], siz[nmax];
inline int getf(int x) {
  while (x != f[x]) x = f[x];
  return x;
}
inline bool merge(int x, int y) {
  x = getf(x);
  y = getf(y);
  if (x == y) return false;
  if (dis[x] > dis[y]) std::swap(x, y);
  f[x] = y;
  sta.push_back(make_pair(x, dis[y]));
  dis[y] += dis[x] == dis[y];
  siz[y] += siz[x];
  return true;
}
inline void undo() {
  const auto pa = sta.back();
  sta.pop_back();
  const int x = pa.first, y = f[x];
  siz[y] -= siz[x];
  f[x] = x;
  dis[y] = pa.second;
}
namespace seg {
vector<int> qs[tmax];
int ll, rr, vv;
void _update(int x, int l, int r) {
  if (ll <= l && r <= rr) return qs[x].push_back(vv);
  const int mid = (l + r) >> 1;
  if (ll <= mid) _update((x << 1), l, mid);
  if (mid < rr) _update(((x << 1) | 1), mid + 1, r);
}
inline void update(int l, int r, int v) {
  ll = l;
  rr = r;
  vv = v;
  _update(1, 1, m);
}
void solve(int x = 1, int l = 1, int r = m) {
  int cnt = 0;
  for (int q : qs[x])
    if (ope[q][1]) cnt += merge(ope[q][1], ope[q][2]);
  if (l == r) {
    if (que[l]) cout < siz[getf(que[l])] < ('\n');
  } else {
    const int mid = (l + r) >> 1;
    solve((x << 1), l, mid);
    solve(((x << 1) | 1), mid + 1, r);
  }
  while (cnt--) undo();
}
}  // namespace seg
int main() {
  cin > n > m > K;
  for (int i = 1; i <= n; ++i) {
    f[i] = i;
    siz[i] = 1;
  }
  int tot = 0, lst = 1;
  for (int i = 1; i <= m; ++i) {
    ope[i][0] = tot;
    switch ((cin.read<int>())) {
      case 1:
        cin > ope[i][1] > ope[i][2];
        break;
      case 2:
        cin > que[i];
        break;
      case 3: {
        ++tot;
        while (lst <= i && ope[lst][0] + K <= tot) {
          if (ope[lst][1]) seg::update(lst, i, lst);
          ++lst;
        }
        break;
      }
    }
  }
  while (lst <= m) {
    if (ope[lst][1]) seg::update(lst, m, lst);
    ++lst;
  }
  seg::solve();
  return 0;
}
