#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>

#define ll long long
#define ld long double
#define endl "\n"
#define fi first
#define se second
#define y1 sadjfskldf
#define p_b push_back
#define sqr(asdf) ((asdf) * (asdf))
using namespace std;
using namespace __gnu_pbds;

const ll N = 2e5 + 200;
const ll MOD = 1e9 + 7;

template <typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
ll n,m,x,y,p[N],l,paket[N],col[N],sz[N],step,a,b;
vector<ll> v[N];
vector<pair<ll, pair<ll, ll> > > c;
void dfs(ll x,ll y)
{
    col[x] = ++step;
    sz[x] = 1;
    for(auto to : v[x]) if(to != y)
    {
        dfs(to, x);
        sz[x] += sz[to];
    }
}
ll get(ll v)
{
    if(p[v] == v) return v;
    return p[v] = get(p[v]);
}
int main()
{
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i = 1; i <= m; i++)
    {
        cin>>l;
        if(l == 1)
        {
            cin>>x>>y;
            p[x] = y;
            v[y].p_b(x);
            c.p_b({l, {x, y}});
        }
        if(l == 2)
        {
            cin>>x;
            paket[++step] = x;
            c.p_b({l, {x, 0}});
        }
        if(l == 3)
        {
            cin>>x>>y;
            c.p_b({l, {x, y}});
        }
    }
    step = 0;
    for(int i = 1; i <= n; i++) if(p[i] == 0) dfs(i, i);
    for(int i = 1; i <= n; i++) p[i] = i;
    for(auto q : c)
    {
        x = q.se.fi;
        y = q.se.se;
        if(q.fi == 1) p[x] = y;
        if(q.fi == 3)
        {
            y = paket[y];
            a = get(x);
            b = get(y);
//            cout<<x<<" "<<y<<" : "<<a<<" "<<b<<endl;
            if(a == b && col[x] + sz[x] > col[y] && col[x] < col[y])
            {
                cout<<"YES"<<endl;
            }
            else cout<<"NO"<<endl;
        }
    }
    return 0;
}


#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using ppi = pair<int, pii>;
using vi = vector<int>;
#define mp make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define all(v) (v).begin(), (v).end()
vector<int> par, tin, tout, doc;
vector<vi> adj;
vector<ppi> q;
vector<bool> is;
int T = 0;
void dfs(int v) {
    tin[v] = T++;
    for (int u : adj[v])
        dfs(u);
    tout[v] = T++;
}
int root(int a) {
    if (par[a] == -1) par[a] = a;
    int _a = a;
    while (a != par[a]) a = par[a];
    par[_a] = a;
    return a;
}
void add(int a, int b) {
    a = root(a);
    b = root(b);
    if (a == b) return;
    if (a > b) swap(a, b);
    par[b] = a;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m;
    cin >> n >> m;
    q.resize(m);
    is.assign(n, true);
    adj.resize(n);
    tin.resize(n);
    tout.resize(n);
    par.assign(n, -1);
    for (ppi& p : q) {
        cin >> p.fi >> p.se.fi;
        if (p.fi != 2)
            cin >> p.se.se;
        else
            p.se.se = 0;
        p.se.fi--;
        p.se.se--;
        if (p.fi == 1) {
            adj[p.se.se].pb(p.se.fi);
            is[p.se.fi] = false;
        }
    }
    for (int i = 0; i < n; i++) {
        if (is[i]) dfs(i);
    }
    for (ppi& p : q) {
        if (p.fi == 1) {
            add(p.se.fi, p.se.se);
        } else if (p.fi == 2) {
            doc.pb(p.se.fi);
        } else if (p.fi == 3) {
            int y = doc[p.se.se];
            int x = p.se.fi;
            if (root(x) != root(y))
                cout << "NO\n";
            else {
                if (tin[x] <= tin[y] && tout[x] >= tout[y])
                    cout << "YES\n";
                else
                    cout << "NO\n";
            }
        }
    }
}


#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using ppi = pair<int, pii>;
using vi = vector<int>;
using vpi = vector<pii>;
#define mp make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define all(v) (v).begin(), (v).end()
vector<int> par, tin, tout, doc, ans;
vector<vi> adj;
vector<vpi> docq;
vector<ppi> q;
vector<bool> is;
int T = 0;
void dfs(int v) {
    tin[v] = T++;
    for (int u : adj[v])
        dfs(u);
    tout[v] = T++;
}
int root(int a) {
    if (par[a] == -1) par[a] = a;
    int _a = a;
    while (a != par[a]) a = par[a];
    par[_a] = a;
    return a;
}
void add(int a, int b) {
    a = root(a);
    b = root(b);
    if (a == b) return;
    if (a > b) swap(a, b);
    par[b] = a;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m;
    cin >> n >> m;
    q.resize(m);
    is.assign(n, true);
    adj.resize(n);
    tin.resize(n);
    tout.resize(n);
    ans.assign(m, -1);
    par.assign(n, -1);
    for (ppi& p : q) {
        cin >> p.fi >> p.se.fi;
        if (p.fi != 2)
            cin >> p.se.se;
        else
            p.se.se = 0;
        p.se.fi--;
        p.se.se--;
        if (p.fi == 1) {
            adj[p.se.se].pb(p.se.fi);
            is[p.se.fi] = false;
        }
        if (p.fi == 2) {
            doc.pb(p.se.fi);
        }
    }
    docq.resize(doc.size());
    for (int i = 0; i < m; i++) {
        ppi p = q[i];
        if (p.fi == 3)
            docq[p.se.se].pb({p.se.fi, i});
    }
    for (int i = 0; i < n; i++) {
        if (is[i]) dfs(i);
    }
    int num = 0;
    for (ppi& p : q) {
        if (p.fi == 1) {
            add(p.se.fi, p.se.se);
        } else if (p.fi == 2) {
            int cur = num++;
            int x = doc[cur];
            for (pii& p : docq[cur]) {
                int y = p.fi;
                if (root(x) != root(y))
                    ans[p.se] = 0;
                else {
                    if (tin[y] <= tin[x] && tout[x] <= tout[y])
                        ans[p.se] = 1;
                    else
                        ans[p.se] = 0;
                }
            }
        }
    }
    for (int i : ans) {
        if (i == -1) continue;
        if (i)
            cout << "YES\n";
        else
            cout << "NO\n";
    }
}


/*
 * Author    : raj1307 - Raj Singh
 * Date      : 24.08.2020
 */

#pragma GCC optimize("Ofast")
// #pragma GCC optimize("unroll-loops")
// #define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

#define fio ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define fr(i, a, b) for(int i = (a); i < (b); ++i)
#define fb(i, b, a) for(int i = (b); i > (a); --i)
#define fill(x,y) memset(x,y,sizeof(x))
#define rr return
#define mp make_pair
#define pb push_back
#define sz(x) ((int)x.size())
#define int long long
#define ll long long
#define ld long double
#define S second
#define F first
#define endl "\n"
#define Endl "\n"
#define all(v) (v).begin(), (v).end()
typedef pair<int, int> pii;
int max(int a,int b){if(a>b) return a; else return b;}
int min(int a,int b){if(a>b) return b; else return a;}
int gcd(int a,int b){ return (b == 0) ? a : gcd(b, a % b);}
inline int lcm(int a,int b){ return a / gcd(a, b) * b;}
inline int normalize(int x,int mod){ x %= mod; if (x < 0) x += mod; return x;}

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
string abc="abcdefghijklmnopqrstuvwxyz";
const double PI=acosl(-1);
const int mod = 1000*1000*1000+7;
//const int mod = 998244353;
const int inf = 1e18+5;

//a and b are assumed to be taken modulo p
inline int add(int a, int b, int p = mod){ int c = a + b; if(c >= p) c -= p; return c; } // expexts c doesn't exceeds 2p
inline int sub(int a, int b, int p = mod){ int c = a - b; if(c < 0) c += p; return c; } // ...
inline int mul(int a, int b, int p = mod){ return (a * 1LL * b) % p; }  // ...

// template<typename T>
// using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
//1. order_of_key(k) : number of elements strictly lesser than k
//2. find_by_order(k) : k-th element in the set
//To make it an ordered_multiset, use pairs of (value, time_of_insertion)
//to distinguish values which are similar.

int floor1(int n,int k){
    if(n%k == 0 || n >= 0)return n/k;
    return (n/k)-1;
}

int ceil1(int n,int k){
    return floor1(n+k-1,k);
}

int powm(int a, int b) {
    int res=1;
    while(b) {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

const int N = 1e5 + 5;

int n,m;

int parent[N],size1[N],parent1[N];

vector<int>g[N];

const int LG = 20;

int tim=0;
int tin[N], tout[N], level[N];

vector< vector<int> > queries;

void make_set(int v) {
    parent[v] = v;
    size1[v] = 1;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        parent[b] = a;
        size1[a] += size1[b];
    }
}

void dfs(int k, int par, int lvl)
{
    tin[k]=++tim;
    level[k]=lvl;
    for(auto it:g[k])
    {
        if(it==par)
            continue;
        dfs(it, k, lvl+1);
    }
    tout[k]=tim;
}

map<int,int>f;
map<int,int>ff;

void solve(){

    cin>>n>>m;

    fr(i,1,n+1) make_set(i);

    fr(i,0,m){
        int t;
        cin>>t;
        if(t==1){
            int x,y;
            cin>>x>>y;
            queries.pb(vector<int>{t,x,y});
            parent1[x]=y;
            g[x].pb(y);
            g[y].pb(x);

        }else if(t==2){
            int x;
            cin>>x;
            queries.pb(vector<int>{t,x});

        }else{
            int x,j;
            cin>>x>>j;
            queries.pb(vector<int>{t,x,j});

        }
    }

    fr(i,1,n+1){
        if(parent1[i]==0){
            dfs(i,0,0);
        }
    }

    int cnt=1;

    fr(i,0,m){
        int t=queries[i][0];
        if(t==1){
            union_sets(queries[i][2],queries[i][1]);

        }else if(t==2){

            f[cnt]=find_set(queries[i][1]);
            ff[cnt]=queries[i][1];
            ++cnt;

        }else{

            if((tin[queries[i][1]]<=tin[f[queries[i][2]]] and tout[queries[i][1]]>=tout[f[queries[i][2]]] and f[queries[i][2]]!=queries[i][1]) or (tin[queries[i][1]]>=tin[ff[queries[i][2]]] and tout[queries[i][1]]<=tout[ff[queries[i][2]]] and ff[queries[i][2]]!=queries[i][1]) or (find_set(queries[i][1])!=find_set(ff[queries[i][2]])))
                cout<<"NO"<<endl;
            else if(tin[queries[i][1]]<=tin[ff[queries[i][2]]] and tout[queries[i][1]]>=tout[ff[queries[i][2]]])
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;

        }
    }

}

signed main() {
    fio;
    #ifndef ONLINE_JUDGE
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        //freopen("error.txt", "w", stderr);
    #endif
    cout<<fixed<<setprecision(10);
    int t=1;
    //cin>>t;  // UNCOMMENT FOR TEST-CASES
    //precompute();
    //int cnt=1;
    while(t--) {
        //cout<<"Case #"<<cnt++<<": ";
        solve();
    }
    return 0;
}


/*
 * Author    : raj1307 - Raj Singh
 * Date      : 24.08.2020
 */

#pragma GCC optimize("Ofast")
// #pragma GCC optimize("unroll-loops")
// #define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

#define fio ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define fr(i, a, b) for(int i = (a); i < (b); ++i)
#define fb(i, b, a) for(int i = (b); i > (a); --i)
#define fill(x,y) memset(x,y,sizeof(x))
#define rr return
#define mp make_pair
#define pb push_back
#define sz(x) ((int)x.size())
#define int long long
#define ll long long
#define ld long double
#define S second
#define F first
#define endl "\n"
#define Endl "\n"
#define all(v) (v).begin(), (v).end()
typedef pair<int, int> pii;
int max(int a,int b){if(a>b) return a; else return b;}
int min(int a,int b){if(a>b) return b; else return a;}
int gcd(int a,int b){ return (b == 0) ? a : gcd(b, a % b);}
inline int lcm(int a,int b){ return a / gcd(a, b) * b;}
inline int normalize(int x,int mod){ x %= mod; if (x < 0) x += mod; return x;}

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
string abc="abcdefghijklmnopqrstuvwxyz";
const double PI=acosl(-1);
const int mod = 1000*1000*1000+7;
//const int mod = 998244353;
const int inf = 1e18+5;

//a and b are assumed to be taken modulo p
inline int add(int a, int b, int p = mod){ int c = a + b; if(c >= p) c -= p; return c; } // expexts c doesn't exceeds 2p
inline int sub(int a, int b, int p = mod){ int c = a - b; if(c < 0) c += p; return c; } // ...
inline int mul(int a, int b, int p = mod){ return (a * 1LL * b) % p; }  // ...

// template<typename T>
// using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
//1. order_of_key(k) : number of elements strictly lesser than k
//2. find_by_order(k) : k-th element in the set
//To make it an ordered_multiset, use pairs of (value, time_of_insertion)
//to distinguish values which are similar.

int floor1(int n,int k){
    if(n%k == 0 || n >= 0)return n/k;
    return (n/k)-1;
}

int ceil1(int n,int k){
    return floor1(n+k-1,k);
}

int powm(int a, int b) {
    int res=1;
    while(b) {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

const int N = 1e5 + 5;

int n,m;

int parent[N],size1[N],parent1[N];

vector<int>g[N];

const int LG = 20;

int tim=0;
//int parent[LG][N];
int tin[N], tout[N], level[N];

vector< vector<int> > queries;

void make_set(int v) {
    parent[v] = v;
    size1[v] = 1;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        parent[b] = a;
        size1[a] += size1[b];
    }
}

void dfs(int k, int par, int lvl)
{
    tin[k]=++tim;
    //parent[0][k]=par;
    level[k]=lvl;
    for(auto it:g[k])
    {
        if(it==par)
            continue;
        dfs(it, k, lvl+1);
    }
    tout[k]=tim;
}

// int walk(int u, int h)
// {
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((h>>i) & 1)
//             u = parent[i][u];
//     }
//     return u;
// }

// void precompute()
// {
//     for(int i=1;i<LG;i++)
//         for(int j=1;j<=n;j++)
//             if(parent[i-1][j])
//                 parent[i][j]=parent[i-1][parent[i-1][j]];
// }

// int LCA(int u, int v)
// {
//     if(level[u]<level[v])
//         swap(u,v);
//     int diff=level[u]-level[v];
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((1<<i) & diff)
//         {
//             u=parent[i][u];
//         }
//     }
//     if(u==v)
//         return u;
//     for(int i=LG-1;i>=0;i--)
//     {
//         if(parent[i][u] && parent[i][u]!=parent[i][v])
//         {
//             u=parent[i][u];
//             v=parent[i][v];
//         }
//     }
//     return parent[0][u];
// }

map<int,int>f;
map<int,int>ff;

void solve(){

    cin>>n>>m;

    fr(i,1,n+1) make_set(i);

    fr(i,0,m){
        int t;
        cin>>t;
        if(t==1){
            int x,y;
            cin>>x>>y;
            queries.pb(vector<int>{t,x,y});
            //union_sets(y,x);
            parent1[x]=y;
            g[x].pb(y);
            g[y].pb(x);

        }else if(t==2){
            int x;
            cin>>x;
            //int u=x;
            //int v=find_set(x);
            queries.pb(vector<int>{t,x});

        }else{
            int x,j;
            cin>>x>>j;
            queries.pb(vector<int>{t,x,j});

        }
    }

    fr(i,1,n+1){
        if(parent1[i]==0){
            dfs(i,0,0);
        }
    }

    // fr(i,1,n+1) cout<<tout[i]<<" ";
    // cout<<endl;

    //precompute();
    int cnt=1;

    fr(i,0,m){
        int t=queries[i][0];
        if(t==1){
            union_sets(queries[i][2],queries[i][1]);

        }else if(t==2){

            f[cnt]=find_set(queries[i][1]);
            ff[cnt]=queries[i][1];
            //cout<<queries[i][1]<<" "<<f[cnt]<<endl;
            ++cnt;

        }else{

            //cout<<tout[queries[i][1]]<<" "<<tout[ff[queries[i][2]]]<<" "<<f[queries[i][2]]<<" "<<queries[i][2]<<endl;

            if((tin[queries[i][1]]<=tin[f[queries[i][2]]] and tout[queries[i][1]]>=tout[f[queries[i][2]]] and f[queries[i][2]]!=queries[i][1]) or (tin[queries[i][1]]>=tin[ff[queries[i][2]]] and tout[queries[i][1]]<=tout[ff[queries[i][2]]] and ff[queries[i][2]]!=queries[i][1]) or (find_set(queries[i][1])!=find_set(ff[queries[i][2]])))
                cout<<"NO"<<endl;
            else if(tin[queries[i][1]]<=tin[ff[queries[i][2]]] and tout[queries[i][1]]>=tout[ff[queries[i][2]]])
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;

        }
    }

}

signed main() {
    fio;
    #ifndef ONLINE_JUDGE
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        //freopen("error.txt", "w", stderr);
    #endif
    cout<<fixed<<setprecision(10);
    int t=1;
    //cin>>t;  // UNCOMMENT FOR TEST-CASES
    //precompute();
    //int cnt=1;
    while(t--) {
        //cout<<"Case #"<<cnt++<<": ";
        solve();
    }
    return 0;
}


/*
 * Author    : raj1307 - Raj Singh
 * Date      : 24.08.2020
 */

#pragma GCC optimize("Ofast")
// #pragma GCC optimize("unroll-loops")
// #define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

#define fio ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define fr(i, a, b) for(int i = (a); i < (b); ++i)
#define fb(i, b, a) for(int i = (b); i > (a); --i)
#define fill(x,y) memset(x,y,sizeof(x))
#define rr return
#define mp make_pair
#define pb push_back
#define sz(x) ((int)x.size())
#define int long long
#define ll long long
#define ld long double
#define S second
#define F first
#define endl "\n"
#define Endl "\n"
#define all(v) (v).begin(), (v).end()
typedef pair<int, int> pii;
int max(int a,int b){if(a>b) return a; else return b;}
int min(int a,int b){if(a>b) return b; else return a;}
int gcd(int a,int b){ return (b == 0) ? a : gcd(b, a % b);}
inline int lcm(int a,int b){ return a / gcd(a, b) * b;}
inline int normalize(int x,int mod){ x %= mod; if (x < 0) x += mod; return x;}

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
string abc="abcdefghijklmnopqrstuvwxyz";
const double PI=acosl(-1);
const int mod = 1000*1000*1000+7;
//const int mod = 998244353;
const int inf = 1e18+5;

//a and b are assumed to be taken modulo p
inline int add(int a, int b, int p = mod){ int c = a + b; if(c >= p) c -= p; return c; } // expexts c doesn't exceeds 2p
inline int sub(int a, int b, int p = mod){ int c = a - b; if(c < 0) c += p; return c; } // ...
inline int mul(int a, int b, int p = mod){ return (a * 1LL * b) % p; }  // ...

// template<typename T>
// using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
//1. order_of_key(k) : number of elements strictly lesser than k
//2. find_by_order(k) : k-th element in the set
//To make it an ordered_multiset, use pairs of (value, time_of_insertion)
//to distinguish values which are similar.

int floor1(int n,int k){
    if(n%k == 0 || n >= 0)return n/k;
    return (n/k)-1;
}

int ceil1(int n,int k){
    return floor1(n+k-1,k);
}

int powm(int a, int b) {
    int res=1;
    while(b) {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

const int N = 1e5 + 5;

int n,m;

int parent[N],size1[N],parent1[N];

vector<int>g[N];

const int LG = 20;

int tim=0;
//int parent[LG][N];
int tin[N], tout[N], level[N];

vector< vector<int> > queries;

void make_set(int v) {
    parent[v] = v;
    size1[v] = 1;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        parent[b] = a;
        size1[a] += size1[b];
    }
}

void dfs(int k, int par, int lvl)
{
    tin[k]=++tim;
    //parent[0][k]=par;
    level[k]=lvl;
    for(auto it:g[k])
    {
        if(it==par)
            continue;
        dfs(it, k, lvl+1);
    }
    tout[k]=tim;
}

// int walk(int u, int h)
// {
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((h>>i) & 1)
//             u = parent[i][u];
//     }
//     return u;
// }

// void precompute()
// {
//     for(int i=1;i<LG;i++)
//         for(int j=1;j<=n;j++)
//             if(parent[i-1][j])
//                 parent[i][j]=parent[i-1][parent[i-1][j]];
// }

// int LCA(int u, int v)
// {
//     if(level[u]<level[v])
//         swap(u,v);
//     int diff=level[u]-level[v];
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((1<<i) & diff)
//         {
//             u=parent[i][u];
//         }
//     }
//     if(u==v)
//         return u;
//     for(int i=LG-1;i>=0;i--)
//     {
//         if(parent[i][u] && parent[i][u]!=parent[i][v])
//         {
//             u=parent[i][u];
//             v=parent[i][v];
//         }
//     }
//     return parent[0][u];
// }

map<int,int>f;
map<int,int>ff;

void solve(){

    cin>>n>>m;

    fr(i,1,n+1) make_set(i);

    fr(i,0,m){
        int t;
        cin>>t;
        if(t==1){
            int x,y;
            cin>>x>>y;
            queries.pb(vector<int>{t,x,y});
            //union_sets(y,x);
            parent1[x]=y;
            //g[x].pb(y);
            g[y].pb(x);

        }else if(t==2){
            int x;
            cin>>x;
            //int u=x;
            //int v=find_set(x);
            queries.pb(vector<int>{t,x});

        }else{
            int x,j;
            cin>>x>>j;
            queries.pb(vector<int>{t,x,j});

        }
    }

    fr(i,1,n+1){
        if(parent1[i]==0){
            dfs(i,0,0);
        }
    }

    // fr(i,1,n+1) cout<<tout[i]<<" ";
    // cout<<endl;

    //precompute();
    int cnt=1;

    fr(i,0,m){
        int t=queries[i][0];
        if(t==1){
            union_sets(queries[i][2],queries[i][1]);

        }else if(t==2){

            f[cnt]=find_set(queries[i][1]);
            ff[cnt]=queries[i][1];
            //cout<<queries[i][1]<<" "<<f[cnt]<<endl;
            ++cnt;

        }else{

            //cout<<tin[queries[i][1]]<<" "<<tin[f[queries[i][2]]]<<" "<<f[queries[i][2]]<<" "<<queries[i][2]<<endl;

            if((tin[queries[i][1]]<=tin[f[queries[i][2]]] and tout[queries[i][1]]>=tout[f[queries[i][2]]] and f[queries[i][2]]!=queries[i][1]) or (tin[queries[i][1]]>=tin[ff[queries[i][2]]] and tout[queries[i][1]]<=tout[ff[queries[i][2]]] and ff[queries[i][2]]!=queries[i][1]))
                cout<<"NO"<<endl;
            else
                cout<<"YES"<<endl;

        }
    }

}

signed main() {
    fio;
    #ifndef ONLINE_JUDGE
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        //freopen("error.txt", "w", stderr);
    #endif
    cout<<fixed<<setprecision(10);
    int t=1;
    //cin>>t;  // UNCOMMENT FOR TEST-CASES
    //precompute();
    //int cnt=1;
    while(t--) {
        //cout<<"Case #"<<cnt++<<": ";
        solve();
    }
    return 0;
}


/*
 * Author    : raj1307 - Raj Singh
 * Date      : 24.08.2020
 */

#pragma GCC optimize("Ofast")
// #pragma GCC optimize("unroll-loops")
// #define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

#define fio ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define fr(i, a, b) for(int i = (a); i < (b); ++i)
#define fb(i, b, a) for(int i = (b); i > (a); --i)
#define fill(x,y) memset(x,y,sizeof(x))
#define rr return
#define mp make_pair
#define pb push_back
#define sz(x) ((int)x.size())
#define int long long
#define ll long long
#define ld long double
#define S second
#define F first
#define endl "\n"
#define Endl "\n"
#define all(v) (v).begin(), (v).end()
typedef pair<int, int> pii;
int max(int a,int b){if(a>b) return a; else return b;}
int min(int a,int b){if(a>b) return b; else return a;}
int gcd(int a,int b){ return (b == 0) ? a : gcd(b, a % b);}
inline int lcm(int a,int b){ return a / gcd(a, b) * b;}
inline int normalize(int x,int mod){ x %= mod; if (x < 0) x += mod; return x;}

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
string abc="abcdefghijklmnopqrstuvwxyz";
const double PI=acosl(-1);
const int mod = 1000*1000*1000+7;
//const int mod = 998244353;
const int inf = 1e18+5;

//a and b are assumed to be taken modulo p
inline int add(int a, int b, int p = mod){ int c = a + b; if(c >= p) c -= p; return c; } // expexts c doesn't exceeds 2p
inline int sub(int a, int b, int p = mod){ int c = a - b; if(c < 0) c += p; return c; } // ...
inline int mul(int a, int b, int p = mod){ return (a * 1LL * b) % p; }  // ...

// template<typename T>
// using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
//1. order_of_key(k) : number of elements strictly lesser than k
//2. find_by_order(k) : k-th element in the set
//To make it an ordered_multiset, use pairs of (value, time_of_insertion)
//to distinguish values which are similar.

int floor1(int n,int k){
    if(n%k == 0 || n >= 0)return n/k;
    return (n/k)-1;
}

int ceil1(int n,int k){
    return floor1(n+k-1,k);
}

int powm(int a, int b) {
    int res=1;
    while(b) {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

const int N = 1e5 + 5;

int n,m;

int parent[N],size1[N],parent1[N];

vector<int>g[N];

const int LG = 20;

int tim=0;
//int parent[LG][N];
int tin[N], tout[N], level[N];

vector< vector<int> > queries;

void make_set(int v) {
    parent[v] = v;
    size1[v] = 1;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        parent[b] = a;
        size1[a] += size1[b];
    }
}

void dfs(int k, int par, int lvl)
{
    tin[k]=++tim;
    //parent[0][k]=par;
    level[k]=lvl;
    for(auto it:g[k])
    {
        if(it==par)
            continue;
        dfs(it, k, lvl+1);
    }
    tout[k]=tim;
}

// int walk(int u, int h)
// {
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((h>>i) & 1)
//             u = parent[i][u];
//     }
//     return u;
// }

// void precompute()
// {
//     for(int i=1;i<LG;i++)
//         for(int j=1;j<=n;j++)
//             if(parent[i-1][j])
//                 parent[i][j]=parent[i-1][parent[i-1][j]];
// }

// int LCA(int u, int v)
// {
//     if(level[u]<level[v])
//         swap(u,v);
//     int diff=level[u]-level[v];
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((1<<i) & diff)
//         {
//             u=parent[i][u];
//         }
//     }
//     if(u==v)
//         return u;
//     for(int i=LG-1;i>=0;i--)
//     {
//         if(parent[i][u] && parent[i][u]!=parent[i][v])
//         {
//             u=parent[i][u];
//             v=parent[i][v];
//         }
//     }
//     return parent[0][u];
// }

map<int,int>f;

void solve(){

    cin>>n>>m;

    fr(i,1,n+1) make_set(i);

    fr(i,0,m){
        int t;
        cin>>t;
        if(t==1){
            int x,y;
            cin>>x>>y;
            queries.pb(vector<int>{t,x,y});
            //union_sets(y,x);
            parent1[x]=y;
            //g[x].pb(y);
            g[y].pb(x);

        }else if(t==2){
            int x;
            cin>>x;
            //int u=x;
            //int v=find_set(x);
            queries.pb(vector<int>{t,x});

        }else{
            int x,j;
            cin>>x>>j;
            queries.pb(vector<int>{t,x,j});

        }
    }

    fr(i,1,n+1){
        if(parent1[i]==0){
            dfs(i,0,0);
        }
    }

    // fr(i,1,n+1) cout<<tout[i]<<" ";
    // cout<<endl;

    //precompute();
    int cnt=1;

    fr(i,0,m){
        int t=queries[i][0];
        if(t==1){
            union_sets(queries[i][2],queries[i][1]);

        }else if(t==2){

            f[cnt]=find_set(queries[i][1]);
            //cout<<queries[i][1]<<" "<<f[cnt]<<endl;
            ++cnt;

        }else{

            //cout<<tin[queries[i][1]]<<" "<<tin[f[queries[i][2]]]<<" "<<f[queries[i][2]]<<" "<<queries[i][2]<<endl;

            if(tin[queries[i][1]]<=tin[f[queries[i][2]]] and tout[queries[i][1]]>=tout[f[queries[i][2]]] and f[queries[i][2]]!=queries[i][1])
                cout<<"NO"<<endl;
            else
                cout<<"YES"<<endl;

        }
    }

}

signed main() {
    fio;
    #ifndef ONLINE_JUDGE
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        //freopen("error.txt", "w", stderr);
    #endif
    cout<<fixed<<setprecision(10);
    int t=1;
    //cin>>t;  // UNCOMMENT FOR TEST-CASES
    //precompute();
    //int cnt=1;
    while(t--) {
        //cout<<"Case #"<<cnt++<<": ";
        solve();
    }
    return 0;
}


/*
 * Author    : raj1307 - Raj Singh
 * Date      : 24.08.2020
 */

#pragma GCC optimize("Ofast")
// #pragma GCC optimize("unroll-loops")
// #define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

#define fio ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define fr(i, a, b) for(int i = (a); i < (b); ++i)
#define fb(i, b, a) for(int i = (b); i > (a); --i)
#define fill(x,y) memset(x,y,sizeof(x))
#define rr return
#define mp make_pair
#define pb push_back
#define sz(x) ((int)x.size())
#define int long long
#define ll long long
#define ld long double
#define S second
#define F first
#define endl "\n"
#define Endl "\n"
#define all(v) (v).begin(), (v).end()
typedef pair<int, int> pii;
int max(int a,int b){if(a>b) return a; else return b;}
int min(int a,int b){if(a>b) return b; else return a;}
int gcd(int a,int b){ return (b == 0) ? a : gcd(b, a % b);}
inline int lcm(int a,int b){ return a / gcd(a, b) * b;}
inline int normalize(int x,int mod){ x %= mod; if (x < 0) x += mod; return x;}

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
string abc="abcdefghijklmnopqrstuvwxyz";
const double PI=acosl(-1);
const int mod = 1000*1000*1000+7;
//const int mod = 998244353;
const int inf = 1e18+5;

//a and b are assumed to be taken modulo p
inline int add(int a, int b, int p = mod){ int c = a + b; if(c >= p) c -= p; return c; } // expexts c doesn't exceeds 2p
inline int sub(int a, int b, int p = mod){ int c = a - b; if(c < 0) c += p; return c; } // ...
inline int mul(int a, int b, int p = mod){ return (a * 1LL * b) % p; }  // ...

// template<typename T>
// using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
//1. order_of_key(k) : number of elements strictly lesser than k
//2. find_by_order(k) : k-th element in the set
//To make it an ordered_multiset, use pairs of (value, time_of_insertion)
//to distinguish values which are similar.

int floor1(int n,int k){
    if(n%k == 0 || n >= 0)return n/k;
    return (n/k)-1;
}

int ceil1(int n,int k){
    return floor1(n+k-1,k);
}

int powm(int a, int b) {
    int res=1;
    while(b) {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

const int N = 1e5 + 5;

int n,m;

int parent[N],size1[N],parent1[N];

vector<int>g[N];

const int LG = 20;

int tim=0;
//int parent[LG][N];
int tin[N], tout[N], level[N];

vector< vector<int> > queries;

void make_set(int v) {
    parent[v] = v;
    size1[v] = 1;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        parent[b] = a;
        size1[a] += size1[b];
    }
}

void dfs(int k, int par, int lvl)
{
    tin[k]=++tim;
    //parent[0][k]=par;
    level[k]=lvl;
    for(auto it:g[k])
    {
        if(it==par)
            continue;
        dfs(it, k, lvl+1);
    }
    tout[k]=tim;
}

// int walk(int u, int h)
// {
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((h>>i) & 1)
//             u = parent[i][u];
//     }
//     return u;
// }

// void precompute()
// {
//     for(int i=1;i<LG;i++)
//         for(int j=1;j<=n;j++)
//             if(parent[i-1][j])
//                 parent[i][j]=parent[i-1][parent[i-1][j]];
// }

// int LCA(int u, int v)
// {
//     if(level[u]<level[v])
//         swap(u,v);
//     int diff=level[u]-level[v];
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((1<<i) & diff)
//         {
//             u=parent[i][u];
//         }
//     }
//     if(u==v)
//         return u;
//     for(int i=LG-1;i>=0;i--)
//     {
//         if(parent[i][u] && parent[i][u]!=parent[i][v])
//         {
//             u=parent[i][u];
//             v=parent[i][v];
//         }
//     }
//     return parent[0][u];
// }

map<int,int>f;
map<int,int>ff;

void solve(){

    cin>>n>>m;

    fr(i,1,n+1) make_set(i);

    fr(i,0,m){
        int t;
        cin>>t;
        if(t==1){
            int x,y;
            cin>>x>>y;
            queries.pb(vector<int>{t,x,y});
            //union_sets(y,x);
            parent1[x]=y;
            g[x].pb(y);
            g[y].pb(x);

        }else if(t==2){
            int x;
            cin>>x;
            //int u=x;
            //int v=find_set(x);
            queries.pb(vector<int>{t,x});

        }else{
            int x,j;
            cin>>x>>j;
            queries.pb(vector<int>{t,x,j});

        }
    }

    fr(i,1,n+1){
        if(parent1[i]==0){
            dfs(i,0,0);
        }
    }

    // fr(i,1,n+1) cout<<tout[i]<<" ";
    // cout<<endl;

    //precompute();
    int cnt=1;

    fr(i,0,m){
        int t=queries[i][0];
        if(t==1){
            union_sets(queries[i][2],queries[i][1]);

        }else if(t==2){

            f[cnt]=find_set(queries[i][1]);
            ff[cnt]=queries[i][1];
            //cout<<queries[i][1]<<" "<<f[cnt]<<endl;
            ++cnt;

        }else{

            //cout<<tout[queries[i][1]]<<" "<<tout[ff[queries[i][2]]]<<" "<<f[queries[i][2]]<<" "<<queries[i][2]<<endl;

            if((tin[queries[i][1]]<=tin[f[queries[i][2]]] and tout[queries[i][1]]>=tout[f[queries[i][2]]] and f[queries[i][2]]!=queries[i][1]) or (tin[queries[i][1]]>=tin[ff[queries[i][2]]] and tout[queries[i][1]]<=tout[ff[queries[i][2]]] and ff[queries[i][2]]!=queries[i][1]) or (find_set(queries[i][1])!=find_set(ff[queries[i][2]])))
                cout<<"NO"<<endl;
            else
                cout<<"YES"<<endl;

        }
    }

}

signed main() {
    fio;
    #ifndef ONLINE_JUDGE
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        //freopen("error.txt", "w", stderr);
    #endif
    cout<<fixed<<setprecision(10);
    int t=1;
    //cin>>t;  // UNCOMMENT FOR TEST-CASES
    //precompute();
    //int cnt=1;
    while(t--) {
        //cout<<"Case #"<<cnt++<<": ";
        solve();
    }
    return 0;
}


/*
 * Author    : raj1307 - Raj Singh
 * Date      : 24.08.2020
 */

#pragma GCC optimize("Ofast")
// #pragma GCC optimize("unroll-loops")
// #define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

#define fio ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define fr(i, a, b) for(int i = (a); i < (b); ++i)
#define fb(i, b, a) for(int i = (b); i > (a); --i)
#define fill(x,y) memset(x,y,sizeof(x))
#define rr return
#define mp make_pair
#define pb push_back
#define sz(x) ((int)x.size())
#define int long long
#define ll long long
#define ld long double
#define S second
#define F first
#define endl "\n"
#define Endl "\n"
#define all(v) (v).begin(), (v).end()
typedef pair<int, int> pii;
int max(int a,int b){if(a>b) return a; else return b;}
int min(int a,int b){if(a>b) return b; else return a;}
int gcd(int a,int b){ return (b == 0) ? a : gcd(b, a % b);}
inline int lcm(int a,int b){ return a / gcd(a, b) * b;}
inline int normalize(int x,int mod){ x %= mod; if (x < 0) x += mod; return x;}

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
string abc="abcdefghijklmnopqrstuvwxyz";
const double PI=acosl(-1);
const int mod = 1000*1000*1000+7;
//const int mod = 998244353;
const int inf = 1e18+5;

//a and b are assumed to be taken modulo p
inline int add(int a, int b, int p = mod){ int c = a + b; if(c >= p) c -= p; return c; } // expexts c doesn't exceeds 2p
inline int sub(int a, int b, int p = mod){ int c = a - b; if(c < 0) c += p; return c; } // ...
inline int mul(int a, int b, int p = mod){ return (a * 1LL * b) % p; }  // ...

// template<typename T>
// using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
//1. order_of_key(k) : number of elements strictly lesser than k
//2. find_by_order(k) : k-th element in the set
//To make it an ordered_multiset, use pairs of (value, time_of_insertion)
//to distinguish values which are similar.

int floor1(int n,int k){
    if(n%k == 0 || n >= 0)return n/k;
    return (n/k)-1;
}

int ceil1(int n,int k){
    return floor1(n+k-1,k);
}

int powm(int a, int b) {
    int res=1;
    while(b) {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

const int N = 1e5 + 5;

int n,m;

int parent[N],size1[N],parent1[N];

vector<int>g[N];

const int LG = 20;

int tim=0;
//int parent[LG][N];
int tin[N], tout[N], level[N];

vector< vector<int> > queries;

void make_set(int v) {
    parent[v] = v;
    size1[v] = 1;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        parent[b] = a;
        size1[a] += size1[b];
    }
}

void dfs(int k, int par, int lvl)
{
    tin[k]=++tim;
    //parent[0][k]=par;
    level[k]=lvl;
    for(auto it:g[k])
    {
        if(it==par)
            continue;
        dfs(it, k, lvl+1);
    }
    tout[k]=tim;
}

// int walk(int u, int h)
// {
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((h>>i) & 1)
//             u = parent[i][u];
//     }
//     return u;
// }

// void precompute()
// {
//     for(int i=1;i<LG;i++)
//         for(int j=1;j<=n;j++)
//             if(parent[i-1][j])
//                 parent[i][j]=parent[i-1][parent[i-1][j]];
// }

// int LCA(int u, int v)
// {
//     if(level[u]<level[v])
//         swap(u,v);
//     int diff=level[u]-level[v];
//     for(int i=LG-1;i>=0;i--)
//     {
//         if((1<<i) & diff)
//         {
//             u=parent[i][u];
//         }
//     }
//     if(u==v)
//         return u;
//     for(int i=LG-1;i>=0;i--)
//     {
//         if(parent[i][u] && parent[i][u]!=parent[i][v])
//         {
//             u=parent[i][u];
//             v=parent[i][v];
//         }
//     }
//     return parent[0][u];
// }

map<int,int>f;
map<int,int>ff;

void solve(){

    cin>>n>>m;

    fr(i,1,n+1) make_set(i);

    fr(i,0,m){
        int t;
        cin>>t;
        if(t==1){
            int x,y;
            cin>>x>>y;
            queries.pb(vector<int>{t,x,y});
            //union_sets(y,x);
            parent1[x]=y;
            //g[x].pb(y);
            g[y].pb(x);

        }else if(t==2){
            int x;
            cin>>x;
            //int u=x;
            //int v=find_set(x);
            queries.pb(vector<int>{t,x});

        }else{
            int x,j;
            cin>>x>>j;
            queries.pb(vector<int>{t,x,j});

        }
    }

    fr(i,1,n+1){
        if(parent1[i]==0){
            dfs(i,0,0);
        }
    }

    // fr(i,1,n+1) cout<<tout[i]<<" ";
    // cout<<endl;

    //precompute();
    int cnt=1;

    fr(i,0,m){
        int t=queries[i][0];
        if(t==1){
            union_sets(queries[i][2],queries[i][1]);

        }else if(t==2){

            f[cnt]=find_set(queries[i][1]);
            ff[cnt]=queries[i][1];
            //cout<<queries[i][1]<<" "<<f[cnt]<<endl;
            ++cnt;

        }else{

            //cout<<tout[queries[i][1]]<<" "<<tout[ff[queries[i][2]]]<<" "<<f[queries[i][2]]<<" "<<queries[i][2]<<endl;

            if((tin[queries[i][1]]<=tin[f[queries[i][2]]] and tout[queries[i][1]]>=tout[f[queries[i][2]]] and f[queries[i][2]]!=queries[i][1]) or (tin[queries[i][1]]>=tin[ff[queries[i][2]]] and tout[queries[i][1]]<=tout[ff[queries[i][2]]] and ff[queries[i][2]]!=queries[i][1]) or(find_set(queries[i][1])!=find_set(ff[queries[i][2]])))
                cout<<"NO"<<endl;
            else
                cout<<"YES"<<endl;

        }
    }

}

signed main() {
    fio;
    #ifndef ONLINE_JUDGE
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        //freopen("error.txt", "w", stderr);
    #endif
    cout<<fixed<<setprecision(10);
    int t=1;
    //cin>>t;  // UNCOMMENT FOR TEST-CASES
    //precompute();
    //int cnt=1;
    while(t--) {
        //cout<<"Case #"<<cnt++<<": ";
        solve();
    }
    return 0;
}


/*
 *	Author: Gurdeep Singh
 *	Codeforces: gdb_18
 *	Codechef: gurdeep1810
**/
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long int ll;
typedef long double ld;
#define mp make_pair
#define ff first
#define ss second
#define bipc __builtin_popcountll		// count number of set bits
#define lb lower_bound				// 1st occurence greater equal to x
#define ub upper_bound				// 1st occurence greater than x
#define mxe *max_element			// maximum element
#define mne *min_element			// minimum element
#define isr is_sorted				// checks if segment is sorted
#define all(x) x.begin(),x.end()
#define pll pair<ll,ll> 			// pair<ll,ll>
#define vll vector<ll>				// vector<ll>
#define vvll vector<vll>			// vector<vector<ll>>
#define vpll vector<pll>			// vector<pair<ll,ll>>
#define vvpll vector<vpll>			// vector<vector<pair<ll,ll>>>
#ifndef gdb_18
#define endl '\n' 				// do not use for interactive problems , not use assert
#endif

/*------------------------------------------- MAPS ----------------------------------------*/
/* map<ll,ll> temp --> insert(mp(a,b)) , size() , empty() , clear() */
/* find(key) returns end() if not found */
/* temp.lower_bound(x) --> for sets and maps */

/*-------------------------------------- PRIORITY QUEUE -----------------------------------*/
#define pqueue priority_queue
/* pqueue<ll> temp --> max heap , pqueue<ll,vll,greater<ll>> temp --> min heap */
/* temp.push() , temp.top() , temp.pop() , temp.size() , temp.empty() */

/*------------------------------------ SETS AND MULTISETS ---------------------------------*/
/* insert() , size() , empty() , lower_bound , upper_bound */
/* temp.lower_bound(x) --> for sets and maps */
/* find(x) iterator to x if exists else end() */
/* erase() deletes element or iterator in set, ALL elements or a iterator in multiset */
/* FIND & ERASE WORK ON ORDERED SETS BUT NOT ON ORDERED MULTISETS */
template<class T>
using oset=tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
/* less<T> --> set , less_equal<T> --> multiset for increasing order */
/* greater<T> --> set , greater_equal<T> --> multiset for decreasing order */
#define fbo find_by_order
#define ook order_of_key
/* find_by_order(x) - iterator to xth element (0 indexed) if exists else returns end() */
/* increasing order: order_of_key(x) - number of items strictly lesser than x */
/* decreasing order: order_of_key(x) - number of items strictly greater than x */

/*--------------------------------- STACKS QUEUES AND DEQUES ------------------------------*/
#define pb push_back
#define ppb pop_back()
#define pf push_front
#define ppf pop_front()
/* size() , empty() works for all but clear() works for deque only */
/* stack<ll> --> push() , top() , pop() */
/* queue<ll> --> push() , pop() , front() , back() */
/* deque<ll> --> push_front() , push_back() , pop_front() ,pop_back() , front() , back() */

/*--------------------------------- CONSTANTS AND FUNCTIONS -------------------------------*/
const ll modulo1=1e9+7;
const ll modulo2=998244353;
const ll inf=1e18;
const ld pi=acos(-1);
inline ll power(ll a,ll b,ll m)
{
	ll res=1;
	while(b)
	{
		if(b&1) res=(res*a)%m;
		a=(a*a)%m; b>>=1;
	}
	return res%m;
}
inline ll inv(ll a,ll m)
{
	return power(a,m-2,m);
}

/*-------------------------------------- CODE BEGINS --------------------------------------*/

const ll N=1e5+5;
const ll M=2e3+5;
ll mod=modulo1;

vvll adj(N);
ll vert[N];
ll pt[N],sz[N];
pair<ll,pll> queries[N];
bool vis[N];
vll tps;
ll timer=0;
ll st[N],en[N];
vll ans(N,-1);
vvpll gotoquery(N);

void mk(ll v)
{
	pt[v]=v;
	sz[v]=1;
	return;
}

ll fs(ll v)
{
	if(pt[v]==v)
		return v;
	return pt[v]=fs(pt[v]);
}

void ms(ll x,ll y)
{
	x=fs(x);
	y=fs(y);
	if(x!=y)
	{
		if(sz[x]<sz[y])
			swap(x,y);
		sz[x]+=sz[y];
		pt[y]=x;
	}
	return;
}

void topsort(ll v)
{
	vis[v]=true;
	for(auto it:adj[v])
		if(!vis[it])
			topsort(it);
	tps.pb(v);
	return;
}

void dfs(ll v)
{
	vis[v]=true;
	timer++;
	st[v]=timer;
	for(auto it:adj[v])
		if(!vis[it])
			dfs(it);
	timer++;
	en[v]=timer;
	return;
}

void solve()
{
	ll n,m;
	cin>>n>>m;
	for(ll i=1;i<=n;i++)
		mk(i);
	for(ll i=1;i<=m;i++)
	{
		ll typ;
		cin>>typ;
		if(typ==1)
		{
			ll x,y;
			cin>>x>>y;
			adj[y].pb(x);
			queries[i]=mp(1,mp(x,y));
		}
		if(typ==2)
		{
			ll x;
			cin>>x;
			queries[i]=mp(2,mp(x,0));
		}
		if(typ==3)
		{
			ll x,y;
			cin>>x>>y;
			gotoquery[y].pb(mp(x,i));
			queries[i]=mp(3,mp(x,y));
		}
	}
	memset(vis,false,sizeof(vis));
	for(ll i=1;i<=n;i++)
		if(!vis[i])
			topsort(i);
	reverse(all(tps));
	memset(vis,false,sizeof(vis));
	timer=0;
	for(auto i:tps)
		if(!vis[i])
			dfs(i);
	ll cnt=1;
	for(ll i=1;i<=m;i++)
	{
		ll typ=queries[i].ff;
		if(typ==1)
			ms(queries[i].ss.ff,queries[i].ss.ss);
		else if(typ==2)
		{
			for(auto it:gotoquery[cnt])
			{
				ll v=it.ss;
				ll sr=it.ff;
				ll si=queries[i].ss.ff;
				if(fs(sr)==fs(si) && (st[si]>=st[sr] && en[si]<=en[sr]))
					ans[v]=1;
				else
					ans[v]=0;
			}
			cnt++;
		}
	}
	for(auto i:ans)
		if(i!=-1)
			cout<<(i==1?"YES":"NO")<<endl;
	return;
}

void precompute()
{
	// cout<<fixed<<setprecision(10);

	return;
}

/*--------------------------------------- CODE ENDS ---------------------------------------*/
signed main()
{
	clock_t clk=clock();
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	#ifdef gdb_18
		freopen("input.txt","rt",stdin);
		freopen("output.txt","wt",stdout);
	#endif
	precompute();
	ll test=1;
	// cin>>test;
	for(ll i=1;i<=test;i++)
	{
		// cout<<"Case #"<<i<<": ";
		// cout<<endl;
		solve();
	}
	#ifdef gdb_18
	cerr<<"Execution Time : "<<(1000.0*((ld)(clock()-clk))/(ld)CLOCKS_PER_SEC)<<"ms\n";
	#endif
	return 0;
}
/*-----------------------------------------------------------------------------------------*/


/*
 *	Author: Gurdeep Singh
 *	Codeforces: gdb_18
 *	Codechef: gurdeep1810
**/
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long int ll;
typedef long double ld;
#define mp make_pair
#define ff first
#define ss second
#define bipc __builtin_popcountll		// count number of set bits
#define lb lower_bound				// 1st occurence greater equal to x
#define ub upper_bound				// 1st occurence greater than x
#define mxe *max_element			// maximum element
#define mne *min_element			// minimum element
#define isr is_sorted				// checks if segment is sorted
#define all(x) x.begin(),x.end()
#define pll pair<ll,ll> 			// pair<ll,ll>
#define vll vector<ll>				// vector<ll>
#define vvll vector<vll>			// vector<vector<ll>>
#define vpll vector<pll>			// vector<pair<ll,ll>>
#define vvpll vector<vpll>			// vector<vector<pair<ll,ll>>>
#ifndef gdb_18
#define endl '\n' 				// do not use for interactive problems , not use assert
#endif

/*------------------------------------------- MAPS ----------------------------------------*/
/* map<ll,ll> temp --> insert(mp(a,b)) , size() , empty() , clear() */
/* find(key) returns end() if not found */
/* temp.lower_bound(x) --> for sets and maps */

/*-------------------------------------- PRIORITY QUEUE -----------------------------------*/
#define pqueue priority_queue
/* pqueue<ll> temp --> max heap , pqueue<ll,vll,greater<ll>> temp --> min heap */
/* temp.push() , temp.top() , temp.pop() , temp.size() , temp.empty() */

/*------------------------------------ SETS AND MULTISETS ---------------------------------*/
/* insert() , size() , empty() , lower_bound , upper_bound */
/* temp.lower_bound(x) --> for sets and maps */
/* find(x) iterator to x if exists else end() */
/* erase() deletes element or iterator in set, ALL elements or a iterator in multiset */
/* FIND & ERASE WORK ON ORDERED SETS BUT NOT ON ORDERED MULTISETS */
template<class T>
using oset=tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
/* less<T> --> set , less_equal<T> --> multiset for increasing order */
/* greater<T> --> set , greater_equal<T> --> multiset for decreasing order */
#define fbo find_by_order
#define ook order_of_key
/* find_by_order(x) - iterator to xth element (0 indexed) if exists else returns end() */
/* increasing order: order_of_key(x) - number of items strictly lesser than x */
/* decreasing order: order_of_key(x) - number of items strictly greater than x */

/*--------------------------------- STACKS QUEUES AND DEQUES ------------------------------*/
#define pb push_back
#define ppb pop_back()
#define pf push_front
#define ppf pop_front()
/* size() , empty() works for all but clear() works for deque only */
/* stack<ll> --> push() , top() , pop() */
/* queue<ll> --> push() , pop() , front() , back() */
/* deque<ll> --> push_front() , push_back() , pop_front() ,pop_back() , front() , back() */

/*--------------------------------- CONSTANTS AND FUNCTIONS -------------------------------*/
const ll modulo1=1e9+7;
const ll modulo2=998244353;
const ll inf=1e18;
const ld pi=acos(-1);
inline ll power(ll a,ll b,ll m)
{
	ll res=1;
	while(b)
	{
		if(b&1) res=(res*a)%m;
		a=(a*a)%m; b>>=1;
	}
	return res%m;
}
inline ll inv(ll a,ll m)
{
	return power(a,m-2,m);
}

/*-------------------------------------- CODE BEGINS --------------------------------------*/

const ll N=1e5+5;
const ll M=2e3+5;
ll mod=modulo1;

vvll adj(N);
ll vert[N];
ll pt[N],sz[N];
pair<ll,pll> queries[N];
bool vis[N];
vll tps;
ll timer=0;
ll st[N],en[N];

void mk(ll v)
{
	pt[v]=v;
	sz[v]=1;
	return;
}

ll fs(ll v)
{
	if(pt[v]==v)
		return v;
	return pt[v]=fs(pt[v]);
}

void ms(ll x,ll y)
{
	x=fs(x);
	y=fs(y);
	if(x!=y)
	{
		if(sz[x]<sz[y])
			swap(x,y);
		sz[x]+=sz[y];
		pt[y]=x;
	}
	return;
}

void topsort(ll v)
{
	vis[v]=true;
	for(auto it:adj[v])
		if(!vis[it])
			topsort(it);
	tps.pb(v);
	return;
}

void dfs(ll v)
{
	vis[v]=true;
	timer++;
	st[v]=timer;
	for(auto it:adj[v])
		if(!vis[it])
			dfs(it);
	timer++;
	en[v]=timer;
	return;
}

void solve()
{
	ll n,m;
	cin>>n>>m;
	for(ll i=1;i<=n;i++)
		mk(i);
	ll cnt=1;
	for(ll i=1;i<=m;i++)
	{
		ll typ;
		cin>>typ;
		if(typ==1)
		{
			ll x,y;
			cin>>x>>y;
			adj[y].pb(x);
			queries[i]=mp(1,mp(x,y));
		}
		if(typ==2)
		{
			ll x;
			cin>>x;
			queries[i]=mp(2,mp(x,0));
			vert[cnt]=x;
			cnt++;
		}
		if(typ==3)
		{
			ll x,y;
			cin>>x>>y;
			queries[i]=mp(3,mp(x,y));
		}
	}
	memset(vis,false,sizeof(vis));
	for(ll i=1;i<=n;i++)
		if(!vis[i])
			topsort(i);
	reverse(all(tps));
	memset(vis,false,sizeof(vis));
	timer=0;
	for(auto i:tps)
		if(!vis[i])
			dfs(i);
	for(ll i=1;i<=m;i++)
	{
		ll typ=queries[i].ff;
		if(typ==1)
			ms(queries[i].ss.ff,queries[i].ss.ss);
		else if(typ==3)
		{
			ll pk=queries[i].ss.ss;
			ll si=vert[pk];
			ll sr=queries[i].ss.ff;
			if(fs(sr)==fs(si) && (st[si]>=st[sr] && en[si]<=en[sr]))
				cout<<"YES"<<endl;
			else
				cout<<"NO"<<endl;
		}
	}
	return;
}

void precompute()
{
	// cout<<fixed<<setprecision(10);

	return;
}

/*--------------------------------------- CODE ENDS ---------------------------------------*/
signed main()
{
	clock_t clk=clock();
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	#ifdef gdb_18
		freopen("input.txt","rt",stdin);
		freopen("output.txt","wt",stdout);
	#endif
	precompute();
	ll test=1;
	// cin>>test;
	for(ll i=1;i<=test;i++)
	{
		// cout<<"Case #"<<i<<": ";
		// cout<<endl;
		solve();
	}
	#ifdef gdb_18
	cerr<<"Execution Time : "<<(1000.0*((ld)(clock()-clk))/(ld)CLOCKS_PER_SEC)<<"ms\n";
	#endif
	return 0;
}
/*-----------------------------------------------------------------------------------------*/


/*
    ____        __
   / __ \____  / /_  ____ _____ ___
  / /_/ / __ \/ __ \/ __ `/ __ `__ \
 / _, _/ /_/ / / / / /_/ / / / / / /
/_/ |_|\____/_/ /_/\__,_/_/ /_/ /_/
*/
#include<bits/stdc++.h>

typedef long long ll ;

#define pll pair<ll , ll >
#define X   first
#define Y   second
#define mp  make_pair
#define pii pair<int , int>
#define vec vector
#define vpii vector<pair<int , int>>
#define i_n   int i = 0 ; i< n ; i++
#define cn  int n ; cin>>n
#define file_io freopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#define migmig ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cina int a[n] ; for(int i = 0 ; i < n ; i++) cin>>a[i]
#define pb push_back

using namespace std;
const int maxn = 1000*100+5 ;
const ll inf = 9223372036854775807 ;

int n , m , mark[maxn] , st[maxn] , cnt = 0 , ft[maxn] , root[maxn] , sz[maxn]  , ans[maxn];
vec<int> adj[maxn]  ;
pair<int , pii > e[maxn] ;
vec<pii> q[maxn] ;
void dfs(int v)
{
	cnt++ ;
	st[v] = cnt ;
	mark[v] = 1 ;
	for(auto u : adj[v])
	{
		if(mark[u] == 0)
		{
			dfs(u) ;
		}
	}
	cnt++ ;
	ft[v] = cnt ;
}

int getroot(int v)
{
	return (root[v] == v) ? v : root[v] = getroot(root[v]) ;
}

void merge(int v , int u)
{
	v = getroot(v) , u = getroot(u) ;

	if(sz[u] < sz[v]) swap(u , v) ;

	sz[u] += sz[v] ;
	root[v] = u ;
}

void initialize()
{
	for(int i = 1 ; i <= n ; i ++ )
	{
		root[i] = i ;
		sz[i] = 1 ;
	}
}

int main()
{
	migmig ;
	cin>>n>>m ;

	cnt = 0 ;
	initialize() ;
	for(int i = 1 ; i <= m ; i ++ )
	{
		int t ;
		cin>>t ;
		if(t == 1)
		{
			int x , y ;
			cin>>x>>y ;
			adj[y].pb(x) ;
			e[i] = {1 , {x , y}} ;
			root[x] = y ;
		}
		if(t == 2)
		{
			cnt ++ ;
			int x ;
			cin>>x ;
			e[i] = {2 , {x , cnt}} ;
		}
		if(t == 3)
		{
			int x , j ;
			cin>>x>> j ;
			e[i] = { 3 , {x , j}} ;
			q[j].pb({ x , i}) ;
		}
	}

	cnt =0 ;
	for(int i = 1 ; i <= n ; i ++ )
	{
		if(mark[i] == 0)
		{
			dfs(getroot(i)) ;
		}
	}

	initialize() ;

	for(int i = 1 ; i <= m ; i ++ )
	{
		int t = e[i].X ;
		if(t == 1)
		{
			int u = e[i].Y.X , v = e[i].Y.Y ;
			if(getroot(u) != getroot(v)) merge(u , v) ;
		}

		if(t == 2)
		{
			int y = e[i].Y.X  , c = e[i].Y.Y;
			for(int j = 0 ; j < q[c].size() ; j ++ )
			{
				int x = q[c][j].X ;
				if(st[x] <= st[y] && ft[x] >= ft[y] && getroot(x) == getroot(y))
				{
					ans[q[c][j].Y] = 1 ;
				}
			}
		}

		if(t == 3)
		{
			if(ans[i] == 1) cout<<"YES" ;
			else cout<<"NO" ;

			cout<<endl ;
		}
	}

}
//////
////////
///////////


/*
    ____        __
   / __ \____  / /_  ____ _____ ___
  / /_/ / __ \/ __ \/ __ `/ __ `__ \
 / _, _/ /_/ / / / / /_/ / / / / / /
/_/ |_|\____/_/ /_/\__,_/_/ /_/ /_/
*/
#include<bits/stdc++.h>

typedef long long ll ;

#define pll pair<ll , ll >
#define X   first
#define Y   second
#define mp  make_pair
#define pii pair<int , int>
#define vec vector
#define vpii vector<pair<int , int>>
#define i_n   int i = 0 ; i< n ; i++
#define cn  int n ; cin>>n
#define file_io freopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#define migmig ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cina int a[n] ; for(int i = 0 ; i < n ; i++) cin>>a[i]
#define pb push_back

using namespace std;
const int maxn = 1000*100+5 ;
const ll inf = 9223372036854775807 ;

int n , m , mark[maxn] , st[maxn] , cnt = 0 , ft[maxn] , root[maxn] , sz[maxn]  , ans[maxn];
vec<int> adj[maxn]  ;
pair<int , pii > e[maxn] ;
vec<pii> q[maxn] ;
void dfs(int v)
{
	cnt++ ;
	st[v] = cnt ;
	mark[v] = 1 ;
	for(auto u : adj[v])
	{
		if(mark[u] == 0)
		{
			dfs(u) ;
		}
	}
	cnt++ ;
	ft[v] = cnt ;
}

int getroot(int v)
{
	return (root[v] == v) ? v : root[v] = getroot(root[v]) ;
}

void merge(int v , int u)
{
	v = getroot(v) , u = getroot(u) ;

	if(sz[u] < sz[v]) swap(u , v) ;

	sz[u] += sz[v] ;
	root[v] = u ;
}

void initialize()
{
	for(int i = 1 ; i <= n ; i ++ )
	{
		root[i] = i ;
		sz[i] = 1 ;
	}
}

int main()
{
	migmig ;
	cin>>n>>m ;

	cnt = 0 ;
	for(int i = 1 ; i <= m ; i ++ )
	{
		int t ;
		cin>>t ;
		if(t == 1)
		{
			int x , y ;
			cin>>x>>y ;
			adj[y].pb(x) ;
			e[i] = {1 , {x , y}} ;
		}
		if(t == 2)
		{
			cnt ++ ;
			int x ;
			cin>>x ;
			e[i] = {2 , {x , cnt}} ;
		}
		if(t == 3)
		{
			int x , j ;
			cin>>x>> j ;
			e[i] = { 3 , {x , j}} ;
			q[j].pb({ x , i}) ;
		}
	}

	cnt =0 ;
	for(int i = 1 ; i <= n ; i ++ )
	{
		if(mark[i] == 0)
		{
			dfs(i) ;
		}
	}

	initialize() ;

	for(int i = 1 ; i <= m ; i ++ )
	{
		int t = e[i].X ;
		if(t == 1)
		{
			int u = e[i].Y.X , v = e[i].Y.Y ;
			if(getroot(u) != getroot(v)) merge(u , v) ;
		}

		if(t == 2)
		{
			int y = e[i].Y.X  , c = e[i].Y.Y;
			for(int j = 0 ; j < q[c].size() ; j ++ )
			{
				int x = q[c][j].X ;
				if(st[x] <= st[y] && ft[x] >= ft[y] && getroot(x) == getroot(y))
				{
					ans[q[c][j].Y] = 1 ;
				}
			}
		}

		if(t == 3)
		{
			if(ans[i] == 1) cout<<"YES" ;
			else cout<<"NO" ;

			cout<<endl ;
		}
	}

}
//////
////////
///////////


/*
    ____        __
   / __ \____  / /_  ____ _____ ___
  / /_/ / __ \/ __ \/ __ `/ __ `__ \
 / _, _/ /_/ / / / / /_/ / / / / / /
/_/ |_|\____/_/ /_/\__,_/_/ /_/ /_/
*/
#include<bits/stdc++.h>

typedef long long ll ;

#define pll pair<ll , ll >
#define X   first
#define Y   second
#define mp  make_pair
#define pii pair<int , int>
#define vec vector
#define vpii vector<pair<int , int>>
#define i_n   int i = 0 ; i< n ; i++
#define cn  int n ; cin>>n
#define file_io freopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#define migmig ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cina int a[n] ; for(int i = 0 ; i < n ; i++) cin>>a[i]
#define pb push_back

using namespace std;
const int maxn = 1000*100+5 ;
const ll inf = 9223372036854775807 ;

int n , m , mark[maxn] , st[maxn] , cnt = 0 , ft[maxn] , root[maxn] , sz[maxn]  , ans[maxn];
vec<int> adj[maxn]  ;
pair<int , pii > e[maxn] ;
vec<pii> q[maxn] ;
void dfs(int v)
{
	cnt++ ;
	st[v] = cnt ;
	mark[v] = 1 ;
	for(auto u : adj[v])
	{
		if(mark[u] == 0)
		{
			dfs(u) ;
		}
	}
	cnt++ ;
	ft[v] = cnt ;
}

int getroot(int v)
{
	return (root[v] == v) ? v : root[v] = getroot(root[v]) ;
}

void merge(int v , int u)
{
	v = getroot(v) , u = getroot(u) ;

	if(sz[u] < sz[v]) swap(u , v) ;

	sz[u] += sz[v] ;
	root[v] = u ;
}

void initialize()
{
	for(int i = 1 ; i <= n ; i ++ )
	{
		root[i] = i ;
		sz[i] = 1 ;
	}
}

int main()
{
	migmig ;
	cin>>n>>m ;

	cnt = 0 ;
	for(int i = 1 ; i <= m ; i ++ )
	{
		int t ;
		cin>>t ;
		if(t == 1)
		{
			int x , y ;
			cin>>x>>y ;
			adj[y].pb(x) ;
			e[i] = {1 , {x , y}} ;
		}
		if(t == 2)
		{
			cnt ++ ;
			int x ;
			cin>>x ;
			e[i] = {2 , {x , cnt}} ;
		}
		if(t == 3)
		{
			int x , j ;
			cin>>x>> j ;
			e[i] = { 3 , {x , j}} ;
			q[j].pb({ x , i}) ;
		}
	}

	cnt =0 ;
	for(int i = 1 ; i <= n ; i ++ )
	{
		if(mark[i] == 0)
		{
			dfs(i) ;
		}
	}

	initialize() ;

	for(int i = 1 ; i <= m ; i ++ )
	{
		int t = e[i].X ;
		if(t == 1)
		{
			int u = e[i].Y.X , v = e[i].Y.Y ;
			if(getroot(u) != getroot(v)) merge(u , v) ;
		}

		if(t == 2)
		{
			int y = e[i].Y.X  , c = e[i].Y.Y;
			for(int j = 0 ; j < q[c].size() ; j ++ )
			{
				int x = q[c][j].X ;
				if(st[x] < st[y] && ft[x] > ft[y] && getroot(x) == getroot(y))
				{
					ans[q[c][j].Y] = 1 ;
				}
			}
		}

		if(t == 3)
		{
			if(ans[i] == 1) cout<<"YES" ;
			else cout<<"NO" ;

			cout<<endl ;
		}
	}

}
//////
////////
///////////


/*
    ____        __
   / __ \____  / /_  ____ _____ ___
  / /_/ / __ \/ __ \/ __ `/ __ `__ \
 / _, _/ /_/ / / / / /_/ / / / / / /
/_/ |_|\____/_/ /_/\__,_/_/ /_/ /_/
*/
#include<bits/stdc++.h>

typedef long long ll ;

#define pll pair<ll , ll >
#define X   first
#define Y   second
#define mp  make_pair
#define pii pair<int , int>
#define vec vector
#define vpii vector<pair<int , int>>
#define i_n   int i = 0 ; i< n ; i++
#define cn  int n ; cin>>n
#define file_io freopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#define migmig ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cina int a[n] ; for(int i = 0 ; i < n ; i++) cin>>a[i]
#define pb push_back

using namespace std;
const int maxn = 1000*100+5 ;
const ll inf = 9223372036854775807 ;

int n , m , mark[maxn] , st[maxn] , cnt = 0 , ft[maxn] , root[maxn] , sz[maxn]  , ans[maxn];
vec<int> adj[maxn]  ;
pair<int , pii > e[maxn] ;
vec<pii> q[maxn] ;
void dfs(int v)
{
	cnt++ ;
	st[v] = cnt ;
	mark[v] = 1 ;
	for(auto u : adj[v])
	{
		if(mark[u] == 0)
		{
			dfs(u) ;
		}
	}
	cnt++ ;
	ft[v] = cnt ;
}

int getroot(int v)
{
	return (root[v] == v) ? v : root[v] = getroot(root[v]) ;
}

void merge(int v , int u)
{
	v = getroot(v) , u = getroot(u) ;

	if(sz[u] < sz[v]) swap(u , v) ;

	sz[u] += sz[v] ;
	root[v] = u ;
}

void initialize()
{
	for(int i = 1 ; i <= n ; i ++ )
	{
		root[i] = i ;
		sz[i] = 1 ;
	}
}

int main()
{
	migmig ;
	cin>>n>>m ;

	cnt = 0 ;
	initialize() ;
	for(int i = 1 ; i <= m ; i ++ )
	{
		int t ;
		cin>>t ;
		if(t == 1)
		{
			int x , y ;
			cin>>x>>y ;
			adj[y].pb(x) ;
			e[i] = {1 , {x , y}} ;
			root[x] = y ;
		}
		if(t == 2)
		{
			cnt ++ ;
			int x ;
			cin>>x ;
			e[i] = {2 , {x , cnt}} ;
		}
		if(t == 3)
		{
			int x , j ;
			cin>>x>> j ;
			e[i] = { 3 , {x , j}} ;
			q[j].pb({ x , i}) ;
		}
	}

	cnt =0 ;
	for(int i = 1 ; i <= n ; i ++ )
	{
		if(mark[i] == 0)
		{
			dfs(getroot(i)) ;
		}
	}

	initialize() ;

	for(int i = 1 ; i <= m ; i ++ )
	{
		int t = e[i].X ;
		if(t == 1)
		{
			int u = e[i].Y.X , v = e[i].Y.Y ;
			if(getroot(u) != getroot(v)) merge(u , v) ;
		}

		if(t == 2)
		{
			int y = e[i].Y.X  , c = e[i].Y.Y;
			for(int j = 0 ; j < q[c].size() ; j ++ )
			{
				int x = q[c][j].X ;
				if(st[x] < st[y] && ft[x] > ft[y] && getroot(x) == getroot(y))
				{
					ans[q[c][j].Y] = 1 ;
				}
			}
		}

		if(t == 3)
		{
			if(ans[i] == 1) cout<<"YES" ;
			else cout<<"NO" ;

			cout<<endl ;
		}
	}

}
//////
////////
///////////


#include <bits/stdc++.h>

#define in(x) scanf("%d", &x)

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], deg_in[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    assert(u);
    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    in(n), in(m);

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        in(type), in(u);
        if (type != 2)
            in(v);
        if (type == 1)
            adj[v].push_back({u, i}), ++deg_in[u];
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 0; i <= n; i++)
        assert(deg_in[i] <= 1);
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            printf(mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

#define in(x) scanf("%d", &x)

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], deg_in[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    in(n), in(m);

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        in(type), in(u);
        if (type != 2)
            in(v);
        if (type == 1)
            adj[v].push_back({u, i}), ++deg_in[u];
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 1; i <= n; i++)
        if (!deg_in[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            printf(mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

#define in(x) scanf("%d", &x)

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], deg_in[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    in(n), in(m);

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        in(type), in(u);
        if (type != 2)
            in(v);
        if (type == 1)
            adj[v].push_back({u, i}), ++deg_in[u];
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 1; i <= n; i++)
        assert(deg_in[i] <= 1);
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            printf(mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

#define in(x) scanf("%d", &x)

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], deg_in[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    in(n), in(m);

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        in(type), in(u);
        if (type != 2)
            in(v);
        if (type == 1)
            adj[v].push_back({u, i}), assert(!deg_in[u]), ++deg_in[u];
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            printf(mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
int sp[maxn][maxl], par[maxn][maxl], got[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 1, int w = 0)
{
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (!is_anc(u, v))
        return inf;

    int res = 0;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        cin >> type >> u;
        if (type != 2)
            cin >> v;
        que.push_back({type, {u, v}});
        if (type == 1)
            adj[v].push_back({u, i});
        else if (type == 2)
            got[++now] = u, inp_time[now] = i;
    }
    dfs();

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            cout << (mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        cin >> type >> u;
        if (type != 2)
            cin >> v;
        if (type == 1)
            adj[v].push_back({u, i});
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);
    if (n == 100000)
        assert(false);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            cout << (mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
int sp[maxn][maxl], par[maxn][maxl], got[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 1, int w = 0)
{
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        cin >> type >> u;
        if (type != 2)
            cin >> v;
        que.push_back({type, {u, v}});
        if (type == 1)
            adj[v].push_back({u, i});
        else if (type == 2)
            got[++now] = u, inp_time[now] = i;
    }
    dfs();

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            cout << (mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        cin >> type >> u;
        if (type != 2)
            cin >> v;
        que.push_back({type, {u, v}});
        if (type == 1)
            adj[v].push_back({u, i});
        else if (type == 2)
            got[++now] = u, inp_time[now] = i;
    }
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            cout << (mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        cin >> type >> u;
        if (type != 2)
            cin >> v;
        if (type == 1)
            adj[v].push_back({u, i});
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            cout << (mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

#define in(x) scanf("%d", &x)

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], deg_in[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        if (!mark[v.first])
            dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    in(n), in(m);

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        in(type), in(u);
        if (type != 2)
            in(v);
        if (type == 1)
            adj[v].push_back({u, i}), ++deg_in[u];
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            printf(mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include <bits/stdc++.h>

#define in(x) scanf("%d", &x)

using namespace std;

const int maxn = 1e5 + 10, maxl = 20, inf = 1e9;
bool mark[maxn];
int sp[maxn][maxl], par[maxn][maxl], got[maxn], inp_time[maxn], n, m;
int tin[maxn], tout[maxn], timer;
vector<pair<int, pair<int, int>>> que;
vector<pair<int, int>> adj[maxn];

void dfs(int u = 1, int p = 0, int w = 0)
{
    p = p ? p : u;
    par[u][0] = p;
    sp[u][0] = w;
    for (int i = 1; i < maxl; i++)
    {
        par[u][i] = par[par[u][i - 1]][i - 1];
        sp[u][i] = max(sp[u][i - 1], sp[par[u][i - 1]][i - 1]);
    }

    assert(!mark[u]);
    mark[u] = true;
    tin[u] = timer++;
    for (auto v : adj[u])
        dfs(v.first, u, v.second);
    tout[u] = timer++;
}

bool is_anc(int u, int v)
{
    return tin[u] <= tin[v] && tout[u] >= tout[v];
}

int get(int u, int v)
{
    if (u == v)
        return -1;
    if (!is_anc(u, v))
        return inf;

    int res = -1;
    for (int i = maxl - 1; ~i; i--)
        if (!is_anc(par[v][i], u))
            res = max(res, sp[v][i]), v = par[v][i];
    return max(res, sp[v][0]);
}

int main()
{
    in(n), in(m);

    for (int i = 0, type, u, v, now = 0; i < m; i++)
    {
        in(type), in(u);
        if (type != 2)
            in(v);
        if (type == 1)
            adj[v].push_back({u, i});
        if (type == 2)
            got[++now] = u, inp_time[now] = i;
        if (type == 3)
            que.push_back({type, {u, v}});
    }
    for (int i = 1; i <= n; i++)
        if (!mark[i])
            dfs(i);

    for (auto q : que)
    {
        int x = q.second.first, id = q.second.second;
        if (q.first == 3)
        {
            int v = got[id], mx = get(x, v);
            printf(mx < inp_time[id] ? "YES\n" : "NO\n");
        }
    }

    return 0;
}


#include<bits/stdc++.h>

typedef long long int lli;
typedef unsigned long long int ulli;
typedef long double ldb;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
#define pb push_back
#define popb pop_back()
#define pf push_front
#define popf pop_front()
#define si size()
#define be begin()
#define en end()
#define all(v) v.be, v.en
#define le length()
#define mp make_pair
#define mt make_tuple
#define acc(v) accumulate(all(v), 0)
#define F first
#define S second
#define INF lli(1e19)

#define rep(i, n) for (lli i = 0; i < n; i++)
#define repz(i, m, n) for (lli i = m; i <= n; i++)
#define repe(i, n) for (lli i = n - 1; i >= 0; i--)
#define repze(i, m, n) for (lli i = n - 1; i >= m; i--)
#define deci(n) fixed << setprecision(n)
#define high(n) __builtin_popcount(n)
#define highll(n) __builtin_popcountll(n)
#define parity(n) __builtin_parity(n)
#define ctz(n) __builtin_ctz(n)
#define lb lower_bound
#define ub upper_bound
#define er equal_range
#define maxe *max_element
#define mine *min_element
#define mod 1000000007
#define mod2 998244353
#define gcd __gcd
#define kira ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)

//#define endl "\n"

inline lli power(lli x, lli y, lli p = mod)
{
    lli res = 1;
    x = x % p;
    while (y > 0)
    {
        if (y & 1)
            res = (res * x) % p;
        y = y >> 1;
        x = (x * x) % p;
    }
    return res;
}

inline lli modadd(lli a, lli b, lli m = mod)
{
    a += b;
    if (a >= m)
        a -= m;
    return a;
}

inline lli modmul(lli a, lli b, lli m = mod)
{
    return ((a % m) * (b % m)) % m;
}

inline lli modi(lli a, lli m = mod) { return power(a, m - 2, m); }
inline lli md(lli x) {if(x>=0)return x; return -x;}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
lli n, m, curr_time;
vector<lli> parent, dep, adj[101010],in,out,doc[101010];
map<pair<lli,lli>,bool> mpa;
vector<pair<lli,pair<lli,lli>>> query;
vector<bool> boss;
lli getP(lli x)
{
    if(parent[x]==x) return x;
    return parent[x]=getP(parent[x]);
}
bool isCon(lli x, lli y)
{
    x = getP(x);
    y = getP(y);
    if(x==y) return 1;
    return 0;
}
void join(lli x, lli y)
{
    x = getP(x);
    y = getP(y);
    if(x==y) return;
    if(dep[x]<dep[y])
        parent[x]=y;
    else
        parent[y]=x;
}

void dfs(lli x)
{
    if(in[x]>=0) return;
    in[x]=curr_time++;
    for(auto neighbour:adj[x])
        dfs(neighbour);
    out[x]=curr_time++;
}

int32_t main()
{
    kira;
    cin>>n>>m;
    rep(i,n)
    {
        parent.pb(i);
        in.pb(-1);
        out.pb(-1);
        dep.pb(1);
        boss.pb(0);
    }
    lli type, h1, h2,docN=0;
    rep(K,m)
    {
        cin>>type;
        if(type==1)
        {
            cin>>h1>>h2;
            h1--;
            h2--;
            adj[h2].pb(h1);
            boss[h1]=1;
            query.pb({1,{h1,h2}});
        }
        else if(type==2)
        {
            cin>>h1;
            h1--;
            query.pb({2,{docN,h1}});
            docN++;
        }
        else
        {
            cin>>h1>>h2;
            h1--, h2--;
            doc[h2].pb(h1);
            query.pb({3,{h1,h2}});
        }
    }
    curr_time = 0;
    rep(i,n)
    {
        if(in[i]<0 and boss[i]==0) dfs(i);
    }

    rep(i,query.size())
    {
        if(query[i].F==1)
            join(query[i].S.F,query[i].S.S);
        else if(query[i].F==2)
        {
            docN=query[i].S.F;
            h1 = query[i].S.S;
            rep(j,doc[docN].si)
            {
//                cout<<doc[docN][j]<<' '<<isCon(h1,doc[docN][j])<<' '<<in[doc[docN][j]]<<' '<<in[h1] <<' '<<out[doc[docN][j]]<<' '<<out[h1];
//                cout<<endl;
                if(isCon(h1,doc[docN][j]) and in[doc[docN][j]]<=in[h1] and out[doc[docN][j]]>=out[h1])
                    mpa[{docN,doc[docN][j]}]=true;
                else mpa[{docN,doc[docN][j]}]=false;
            }
        }
        else
        {
//            cout<<(mpa.find({query[i].S.S,query[i].S.F})==mpa.end())<<endl;
            if(mpa.find({query[i].S.S,query[i].S.F})==mpa.end() or mpa[{query[i].S.S,query[i].S.F}]==false) cout<<"NO"<<endl;
            else cout<<"YES"<<endl;
        }
    }

    return 0;

}


#include<bits/stdc++.h>

typedef long long int lli;
typedef unsigned long long int ulli;
typedef long double ldb;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
#define pb push_back
#define popb pop_back()
#define pf push_front
#define popf pop_front()
#define si size()
#define be begin()
#define en end()
#define all(v) v.be, v.en
#define le length()
#define mp make_pair
#define mt make_tuple
#define acc(v) accumulate(all(v), 0)
#define F first
#define S second
#define INF lli(1e19)

#define rep(i, n) for (lli i = 0; i < n; i++)
#define repz(i, m, n) for (lli i = m; i <= n; i++)
#define repe(i, n) for (lli i = n - 1; i >= 0; i--)
#define repze(i, m, n) for (lli i = n - 1; i >= m; i--)
#define deci(n) fixed << setprecision(n)
#define high(n) __builtin_popcount(n)
#define highll(n) __builtin_popcountll(n)
#define parity(n) __builtin_parity(n)
#define ctz(n) __builtin_ctz(n)
#define lb lower_bound
#define ub upper_bound
#define er equal_range
#define maxe *max_element
#define mine *min_element
#define mod 1000000007
#define mod2 998244353
#define gcd __gcd
#define kira ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)

//#define endl "\n"

inline lli power(lli x, lli y, lli p = mod)
{
    lli res = 1;
    x = x % p;
    while (y > 0)
    {
        if (y & 1)
            res = (res * x) % p;
        y = y >> 1;
        x = (x * x) % p;
    }
    return res;
}

inline lli modadd(lli a, lli b, lli m = mod)
{
    a += b;
    if (a >= m)
        a -= m;
    return a;
}

inline lli modmul(lli a, lli b, lli m = mod)
{
    return ((a % m) * (b % m)) % m;
}

inline lli modi(lli a, lli m = mod) { return power(a, m - 2, m); }
inline lli md(lli x) {if(x>=0)return x; return -x;}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
lli n, m, curr_time;
vector<lli> parent, dep, adj[101010],in,out,doc;
vector<bool> pos;
vector<pair<lli,lli>> query;
lli getP(lli x)
{
    if(parent[x]==x) return x;
    return parent[x]=getP(parent[x]);
}
bool isCon(lli x, lli y)
{
    x = getP(x);
    y = getP(y);
    if(x==y) return 1;
    return 0;
}
void join(lli x, lli y)
{
    x = getP(x);
    y = getP(y);
    if(x==y) return;
    if(dep[x]<dep[y])
        parent[x]=y;
    else
        parent[y]=x;
}

void dfs(lli x)
{
    if(in[x]>=0) return;
    in[x]=curr_time++;
    for(auto neighbour:adj[x])
        dfs(neighbour);
    out[x]=curr_time++;
}

int32_t main()
{
    kira;
    cin>>n>>m;
    rep(i,n)
    {
        parent.pb(i);
        in.pb(-1);
        out.pb(-1);
        dep.pb(1);
    }
    lli type, h1, h2;
    rep(K,m)
    {
        cin>>type;
        if(type==1)
        {
            cin>>h1>>h2;
            h1--;
            h2--;
            adj[h2].pb(h1);
            join(h2,h1);
        }
        else if(type==2)
        {
            cin>>h1;
            h1--;
            doc.pb(h1);
        }
        else
        {
            cin>>h1>>h2;
            h1--, h2--;
            if(h2>=doc.size()) {pos.pb(false); query.pb({-1, -1}); continue;}
            if(isCon(h1,doc[h2]))
            {
                query.pb({h1,doc[h2]});
                pos.pb(true);
            }
            else
            {
                query.pb({h1,doc[h2]});
                pos.pb(false);
            }
        }
    }
    curr_time = 0;
    rep(i,n)
    {
        if(in[i]<0 and parent[i]==i) dfs(i);
    }
    rep(i,query.size())
    {
//        cout<<pos[i]<<endl;
//        cout<<in[query[i].F]<<' '<<in[query[i].S]<<' '<<out[query[i].F]<<' '<<out[query[i].S]<<endl;
        if(pos[i]==false) cout<<"NO"<<endl;
        else{
            if (in[query[i].F] <= in[query[i].S] and out[query[i].F] >= out[query[i].S]) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
    }

    return 0;

}


#include<bits/stdc++.h>

typedef long long int lli;
typedef unsigned long long int ulli;
typedef long double ldb;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
#define pb push_back
#define popb pop_back()
#define pf push_front
#define popf pop_front()
#define si size()
#define be begin()
#define en end()
#define all(v) v.be, v.en
#define le length()
#define mp make_pair
#define mt make_tuple
#define acc(v) accumulate(all(v), 0)
#define F first
#define S second
#define INF lli(1e19)

#define rep(i, n) for (lli i = 0; i < n; i++)
#define repz(i, m, n) for (lli i = m; i <= n; i++)
#define repe(i, n) for (lli i = n - 1; i >= 0; i--)
#define repze(i, m, n) for (lli i = n - 1; i >= m; i--)
#define deci(n) fixed << setprecision(n)
#define high(n) __builtin_popcount(n)
#define highll(n) __builtin_popcountll(n)
#define parity(n) __builtin_parity(n)
#define ctz(n) __builtin_ctz(n)
#define lb lower_bound
#define ub upper_bound
#define er equal_range
#define maxe *max_element
#define mine *min_element
#define mod 1000000007
#define mod2 998244353
#define gcd __gcd
#define kira ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)

//#define endl "\n"

inline lli power(lli x, lli y, lli p = mod)
{
    lli res = 1;
    x = x % p;
    while (y > 0)
    {
        if (y & 1)
            res = (res * x) % p;
        y = y >> 1;
        x = (x * x) % p;
    }
    return res;
}

inline lli modadd(lli a, lli b, lli m = mod)
{
    a += b;
    if (a >= m)
        a -= m;
    return a;
}

inline lli modmul(lli a, lli b, lli m = mod)
{
    return ((a % m) * (b % m)) % m;
}

inline lli modi(lli a, lli m = mod) { return power(a, m - 2, m); }
inline lli md(lli x) {if(x>=0)return x; return -x;}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
lli n, m, curr_time;
vector<lli> parent, dep, adj[101010],in,out,doc;
vector<bool> pos;
vector<pair<lli,lli>> query;
lli getP(lli x)
{
    if(parent[x]==x) return x;
    return parent[x]=getP(parent[x]);
}
bool isCon(lli x, lli y)
{
    x = getP(x);
    y = getP(y);
    if(x==y) return 1;
    return 0;
}
void join(lli x, lli y)
{
    x = getP(x);
    y = getP(y);
    if(x==y) return;
    if(dep[x]<dep[y])
        parent[x]=y;
    else
        parent[y]=x;
}

void dfs(lli x)
{
    if(in[x]>=0) return;
    in[x]=curr_time++;
    for(auto neighbour:adj[x])
        dfs(neighbour);
    out[x]=curr_time++;
}

int32_t main()
{
    kira;
    cin>>n>>m;
    rep(i,n)
    {
        parent.pb(i);
        in.pb(-1);
        out.pb(-1);
        dep.pb(1);
    }
    lli type, h1, h2;
    rep(K,m)
    {
        cin>>type;
        if(type==1)
        {
            cin>>h1>>h2;
            h1--;
            h2--;
            adj[h2].pb(h1);
            join(h2,h1);
        }
        else if(type==2)
        {
            cin>>h1;
            h1--;
            doc.pb(h1);
        }
        else
        {
            cin>>h1>>h2;
            h1--, h2--;
            if(h2>=doc.size()) {pos.pb(false); query.pb({-1, -1}); continue;}
            if(isCon(h1,doc[h2]))
            {
                query.pb({h1,doc[h2]});
                pos.pb(true);
            }
            else
            {
                query.pb({h1,doc[h2]});
                pos.pb(false);
            }
        }
    }
    curr_time = 0;
    rep(i,n)
    {
        if(in[i]<0) dfs(i);
    }
    rep(i,query.size())
    {
        if(pos[i]==false) cout<<"NO"<<endl;
        else{
            if (in[query[i].F] <= in[query[i].S] and out[query[i].F] >= out[query[i].S]) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
    }

    return 0;

}


/****** BISMIintAH HIR RAHMANIR RAHIM ******/

#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/detail/standard_policies.hpp>

using namespace std;
using namespace __gnu_pbds;
using namespace __gnu_cxx;

typedef long long ll;
typedef pair <int, int> pii;
/* Special functions:

        find_by_order(k) --> returns iterator to the kth largest element counting from 0
        order_of_key(val) --> returns the number of items in a set that are strictly smainter than our item
*/

typedef tree<
int,
null_type,
less<int>,
rb_tree_tag,
tree_order_statistics_node_update>
ordered_set;

#define EPS 1e-9
#define NL cout<<endl;
#define MP make_pair
#define Aint(a) a.begin(), a.end()
#define DBG(a) cout<< #a <<" --> "<<(a)<<endl;
#define FastIO ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define IN freopen("input.txt","r+",stdin)
#define OUT freopen("output.txt","w+",stdout)

template < class T1,class T2>
ostream &operator <<(ostream &os,const pair < T1,T2 > &p)
{
    os<<"{"<<p.first<<","<<p.second<<"}";
    return os;
}
template < class T1,class T2>
pair <T1,T2> operator + ( const  pair < T1,T2 > &p1,const pair < T1,T2 > &p2)
{
    return make_pair(p1.first+p2.first,p1.second+p2.second);
}
/****** END OF HEADER ******/

#define mx 100005

int parent[mx];
int rnk[mx];
int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void make_set(int v) {
    parent[v] = v;
    rnk[v] = 0;
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        if (rnk[a] < rnk[b])
            swap(a, b);
        parent[b] = a;
        if (rnk[a] == rnk[b])
            rnk[a]++;
    }
}

int L[mx]; //লেভেল
int P[mx][22]; //স্পার্স টেবিল
int T[mx]; //প্যারেন্ট

int givento[mx];
int indeg[mx];
vector<int>g[mx];
void dfs(int from,int u,int dep)
{
    T[u]=from;
    L[u]=dep;
    for(int i=0; i<(int)g[u].size(); i++)
    {

        int v=g[u][i];
        if(v==from)
            continue;
        dfs(u,v,dep+1);
    }
}

int lca_query(int N, int p, int q) //N=নোড সংখ্যা
{
    int tmp, log, i;

    if (L[p] < L[q])
        tmp = p, p = q, q = tmp;

    log=1;
    while(1)
    {
        int next=log+1;
        if((1<<next)>L[p])
            break;
        log++;
    }

    for (i = log; i >= 0; i--)
        if (L[p] - (1 << i) >= L[q])
            p = P[p][i];

    if (p == q)
        return p;

    for (i = log; i >= 0; i--)
        if (P[p][i] != -1 && P[p][i] != P[q][i])
            p = P[p][i], q = P[q][i];

    return T[p];
}

void lca_init(int N)
{
    memset (P,-1,sizeof(P)); //শুরুতে সবগুলো ঘরে -১ থাকবে
    int i, j;
    for (i = 0; i < N; i++)
        P[i][0] = T[i];

    for (j = 1; 1 << j < N; j++)
        for (i = 0; i < N; i++)
            if (P[i][j - 1] != -1)
            {
                P[i][j] = P[P[i][j - 1]][j - 1];
            }

}

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++) make_set(i);
    vector < pair<int ,pii> > inp;
    for(int i=0;i<m;i++)
    {
        int t,x,y;
        cin>>t>>x;
        if(t!=2) cin>>y;
        x--;
        if(t==1) y--;
        inp.push_back({t,{x,y}});
        if(t==1)
        {
            g[y].push_back(x);
            indeg[x]++;
        }
    }
    int totalpacket=1;
    memset(T,-1,sizeof T);
    for(int i=0;i<n;i++)
    {
        if(indeg[i]==0 && T[i]==-1)
        {
            dfs(-1,i,0);
        }
    }
    lca_init(n);
    for(pair<int ,pii>  i : inp)
    {
        int t= i.first,x= i.second.first,y=i.second.second;
        if(t==2)
        {
            givento[totalpacket++]=x;
        }
        else if(t==3)
        {
            int ans=0;
            if(find_set(x)==find_set(givento[y]))
            {
                int lc=lca_query(n,x,givento[y]);
                if(lc==x) ans=1;
            }
            cout<<(ans ? "YES\n" : "NO\n");
        }
        else
        {
            union_sets(x,y);
        }
    }

}