#include <bits/stdc++.h>   using namespace std;   template <class T>   struct rge {     T b, e;   };   template <class T>   rge<T> range(T i, T j) {     return rge<T>{i, j};   }   template <class T>   auto dud(T *x) -> decltype(cerr << *x, 0);   template <class T>   char dud(...);   struct debug {     template <class T>     debug &operator<<(const T &) {       return *this;     }   };   const int N = 1e5 + 5;   const int M = N;   const int inf = 1e9 + 7;   struct dsu {     vector<int> pr;     dsu(int n) : pr(n + 5) { iota(pr.begin(), pr.end(), 0); }     int find(int u) { return pr[u] == u ? u : pr[u] = find(pr[u]); }     void unite(int u, int v) {       u = find(u), v = find(v);       if (u != v) {         pr[v] = u;         debug() <<  the boss of                                         <<  v                      :                   << (v)                 <<                         is                                         <<  u                      :                   << (u) <<    ;       }     }   };   struct query {     int t, u, v, doc;   };   vector<int> graph[N];   int pr[N], dep[N], tot[N], heavy[N];   int head[N], pos[N], who[N];   set<pair<int, int> > st[N];   vector<query> queries;   int indeg[N];   int timer = 0;   int n, m;   int dfs(int u) {     tot[u] = 1, heavy[u] = head[u] = 0;     for (int v : graph[u])       if (v != pr[u]) {         pr[v] = u;         dep[v] = dep[u] + 1;         tot[u] += dfs(v);         if (tot[v] > tot[heavy[u]]) heavy[u] = v;       }     return tot[u];   }   void hld(int u, int h) {     head[u] = h, pos[u] = ++timer, who[timer] = u;     if (heavy[u] != 0) hld(heavy[u], h);     for (int v : graph[u])       if (v != pr[u] && v != heavy[u]) hld(v, v);   }   template <class F>   void process_path(int u, int v, F fun) {     for (; head[u] != head[v]; v = pr[head[v]]) {       if (dep[head[u]] > dep[head[v]]) swap(u, v);       fun(pos[head[v]], pos[v]);     }     if (dep[u] > dep[v]) swap(u, v);     if (u != v) fun(pos[heavy[u]], pos[v]);     fun(pos[u], pos[u]);   }   void update_path(int u, int v, int doc) {     debug() <<  sign from                                 <<  u                  :               << (u)             <<                     to                                 <<  v                  :               << (v)             <<                                    <<  doc                  :               << (doc) <<    ;     process_path(u, v, [&](int l, int r) { st[doc].insert({l, r}); });   }   int main() {     ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);     cin >> n >> m;     int doc = 0;     for (auto i = (0) - ((0) > (m)); i != m - ((0) > (m));          i += 1 - 2 * ((0) > (m))) {       query q;       cin >> q.t >> q.u;       if (q.t == 1) {         cin >> q.v;         swap(q.u, q.v);         graph[q.u].push_back(q.v);         indeg[q.v]++;       } else if (q.t == 2) {         q.doc = ++doc;         st[doc].insert({-inf, -inf});         st[doc].insert({inf, inf});       } else if (q.t == 3)         cin >> q.doc;       queries.push_back(q);     }     for (auto u = (1) - ((1) > (n + 1)); u != n + 1 - ((1) > (n + 1));          u += 1 - 2 * ((1) > (n + 1)))       if (indeg[u] == 0) {         dfs(u);         hld(u, u);       }     dsu boss(n);     for (auto &q : queries) {       if (q.t == 1)         boss.unite(q.u, q.v);       else if (q.t == 2)         update_path(q.u, boss.find(q.u), q.doc);       else {         debug() <<                     <<  st[q.doc]                      :                   << (st[q.doc]) <<    ;         int k = pos[q.u];         auto it = --st[q.doc].upper_bound({k, inf});         debug() <<                     <<  k                      :                   << (k)                 <<                                            <<  *it                      :                   << (*it) <<    ;         for (auto jt : {prev(it), it, next(it)})           if (jt != st[q.doc].end())             if (jt->first <= k && k <= jt->second) {               k = inf;               break;             }         cout << (k == inf ?  YES  :  NO ) <<   n ;       }     }     return 0;   }