#include <bits/stdc++.h>
using namespace std;
struct UnionFind {
  int n;
  vector<int> p, rank;
  int findSet(int i) { return (p[i] == i ? i : (p[i] = findSet(p[i]))); }
  bool isSameSet(int i, int j) { return (findSet(i) == findSet(j)); }
  void unionSet(int i, int j) {
    if (!isSameSet(i, j)) {
      int x = findSet(i);
      int y = findSet(j);
      if (rank[x] > rank[y]) {
        p[y] = x;
      } else {
        p[x] = y;
      }
      if (rank[x] == rank[y]) {
        rank[y]++;
      }
    }
  }
  UnionFind(int _n) {
    n = _n;
    for (int i = 0; i < n; i++) {
      p.push_back(i);
      rank.push_back(0);
    }
  }
};
int n, m;
vector<int> P;
vector<int> type;
vector<pair<int, int> > query;
vector<pair<int, int> > t3;
vector<int> H;
void calculateHeight() {
  vector<int> inDegree(n, 0);
  for (int i = 0; i < n; i++) {
    if (P[i] != -1) {
      inDegree[P[i]] += 1;
    }
  }
  H.resize(n, -1);
  queue<int> q;
  for (int i = 0; i < n; i++) {
    if (inDegree[i] == 0) {
      H[i] = 0;
      q.push(i);
    }
  }
  while (!q.empty()) {
    int u = q.front();
    q.pop();
    if (P[u] == -1) {
      continue;
    }
    H[P[u]] = max(H[P[u]], H[u] + 1);
    inDegree[P[u]] -= 1;
    if (inDegree[P[u]] == 0) {
      q.push(P[u]);
    }
  }
}
set<pair<int, int> > isParent;
vector<vector<int> > G;
vector<vector<int> > Q;
vector<int> visited;
void dfs(int u) {
  visited[u] = true;
  for (int i = 0; i < Q[u].size(); i++) {
    int p = Q[u][i];
    if (visited[p]) {
      isParent.insert(make_pair(p, u));
    }
  }
  for (int i = 0; i < G[u].size(); i++) {
    dfs(G[u][i]);
  }
  visited[u] = false;
}
int main() {
  cin >> n >> m;
  P.resize(n, -1);
  G.resize(n);
  Q.resize(n);
  visited.resize(n, false);
  type.resize(m);
  query.resize(m);
  for (int i = 0; i < m; i++) {
    cin >> type[i];
    if (type[i] == 1) {
      cin >> query[i].first >> query[i].second;
      query[i].first -= 1;
      query[i].second -= 1;
      P[query[i].first] = query[i].second;
      G[query[i].second].push_back(query[i].first);
    } else if (type[i] == 2) {
      cin >> query[i].first;
      query[i].first -= 1;
    } else {
      cin >> query[i].first >> query[i].second;
      query[i].first -= 1;
      query[i].second -= 1;
      t3.push_back(make_pair(query[i].second, i));
    }
  }
  int t3i = 0;
  int doc = 0;
  sort(t3.begin(), t3.end());
  for (int i = 0; i < m; i++) {
    if (type[i] == 2) {
      for (; t3i < t3.size() && t3[t3i].first == doc; t3i++) {
        int c = query[i].first;
        int p = query[t3[t3i].second].first;
        Q[c].push_back(p);
      }
      doc++;
    }
  }
  for (int i = 0; i < n; i++) {
    if (P[i] == -1) {
      dfs(i);
    }
  }
  UnionFind uf(n);
  t3i = 0;
  doc = 0;
  for (int i = 0; i < m; i++) {
    if (type[i] == 1) {
      uf.unionSet(query[i].first, query[i].second);
    } else if (type[i] == 2) {
      for (; t3i < t3.size() && t3[t3i].first == doc; t3i++) {
        int c = query[i].first;
        int p = query[t3[t3i].second].first;
        if (uf.isSameSet(c, p) && isParent.count(make_pair(p, c))) {
          query[t3[t3i].second].second = true;
        } else {
          query[t3[t3i].second].second = false;
        }
      }
      doc++;
    }
  }
  for (int i = 0; i < m; i++) {
    if (type[i] == 3) {
      if (query[i].second) {
        cout << "YES\n";
      } else {
        cout << "NO\n";
      }
    }
  }
}
