#include <bits/stdc++.h>
using namespace std;
int n, m;
struct request1 {
  int num;
  int x;
  int y;
  request1(){};
  request1(int nnum, int xx, int yy) {
    num = nnum;
    x = xx;
    y = yy;
  }
};
struct request2 {
  int num;
  int people;
  request2(){};
  request2(int nnum, int ppeople) {
    num = nnum;
    people = ppeople;
  }
};
struct request3 {
  int num;
  int document;
  int x;
  bool ans;
  request3(){};
  request3(int nnum, int ddocument, int xx) {
    num = nnum;
    document = ddocument;
    x = xx;
  }
};
vector<request1> First;
vector<request2> Second;
vector<request3> Third;
void init() {
  cin >> n >> m;
  for (int i = 0; i < m; ++i) {
    int type;
    cin >> type;
    if (type == 1) {
      int x, y;
      cin >> x >> y;
      --x;
      --y;
      First.push_back(request1(i, x, y));
    }
    if (type == 2) {
      int people;
      cin >> people;
      --people;
      Second.push_back(request2(i, people));
    }
    if (type == 3) {
      int document, x;
      cin >> x >> document;
      --document;
      --x;
      Third.push_back(request3(i, document, x));
    }
  }
}
void dfs(int v, vector<int> &list, vector<bool> &used,
         vector<vector<int> > &g) {
  used[v] = true;
  for (int i = 0; i < g[v].size(); ++i) {
    int to = g[v][i];
    if (!used[to]) dfs(to, list, used, g);
  }
  list.push_back(v);
}
void topological_rename() {
  vector<vector<int> > g;
  g.resize(n);
  for (int i = 0; i < First.size(); ++i) {
    g[First[i].y].push_back(First[i].x);
  }
  vector<int> list;
  vector<bool> used;
  used.assign(n, false);
  for (int i = 0; i < n; ++i)
    if (!used[i]) dfs(i, list, used, g);
  vector<int> listt(n);
  for (int i = 0; i < n; ++i) listt[list[i]] = i;
  list = listt;
  for (int i = 0; i < First.size(); ++i) {
    First[i].x = n - list[First[i].x] - 1;
    First[i].y = n - list[First[i].y] - 1;
  }
  for (int i = 0; i < Second.size(); ++i) {
    Second[i].people = n - list[Second[i].people] - 1;
  }
  for (int i = 0; i < Third.size(); ++i) {
    Third[i].x = n - list[Third[i].x] - 1;
  }
}
vector<vector<int> > bin_up;
bool check(int son, int father) {
  if (father > son) return false;
  if (father == son) return true;
  int cur = -1;
  int prefather = n + 1;
  while (prefather > father) {
    ++cur;
    prefather = bin_up[son][cur];
    if (prefather == -1) {
      if (cur == 0) return false;
      bool ans = check(bin_up[son][cur - 1], father);
      bin_up[son][cur] = bin_up[bin_up[son][cur - 1]][cur - 1];
      return ans;
    }
  }
  if (prefather == father) return true;
  if (cur > 0)
    return check(bin_up[son][cur - 1], father);
  else
    return false;
}
bool cmpdocum(const request3 &a, const request3 &b) {
  return (a.document < b.document);
}
bool cmpnum(const request3 &a, const request3 &b) { return (a.num < b.num); }
int main() {
  init();
  topological_rename();
  bin_up.assign(n, vector<int>(20, -1));
  sort(Third.begin(), Third.end(), &cmpdocum);
  int i1 = 0;
  int i2 = 0;
  int i3 = 0;
  while (i1 < First.size() || i2 < Second.size()) {
    if ((i1 < First.size()) &&
        (i2 >= Second.size() || First[i1].num < Second[i2].num)) {
      int x = First[i1].x;
      int y = First[i1].y;
      bin_up[x][0] = y;
      ++i1;
    } else {
      while (i3 < Third.size() && Third[i3].document == i2) {
        if (check(Second[i2].people, Third[i3].x))
          Third[i3].ans = true;
        else
          Third[i3].ans = false;
        ++i3;
      }
      ++i2;
    }
  }
  sort(Third.begin(), Third.end(), &cmpnum);
  for (int i = 0; i < Third.size(); ++i)
    if (Third[i].ans)
      cout << "YES\n";
    else
      cout << "NO\n";
}
