#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <cmath>
#include <bitset>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <algorithm>
#include <complex>
#include <unordered_map>
#include <unordered_set>
#include <random>
#include <cassert>
#include <fstream>
#include <utility>
#include <functional>
#include <time.h>
#include <stack>
#define popcount __builtin_popcount
using namespace std;
typedef long long int ll;
typedef pair<ll, ll> P;
struct UnionFindUndo{
    int n;
    vector<int> data;
    stack<pair<int, int>> st;
    UnionFindUndo(){}
    UnionFindUndo(int n):n(n), data(n, -1){}
    void init(int v){
        n=v;
        data.resize(n, -1);
    }
    int find(int x){
        if(data[x]<0) return x;
        return find(data[x]);
    }
    void unite(int x, int y){
        x=find(x), y=find(y);
        st.push({x, data[x]});
        st.push({y, data[y]});
        if(x!=y){
            if(data[x]>data[y]) swap(x, y);
            data[x]+=data[y];
            data[y]=x;
        }
    }
    bool same(int x, int y){
        return find(x)==find(y);
    }
    void undo(){
        data[st.top().first]=st.top().second;
        st.pop();
        data[st.top().first]=st.top().second;
        st.pop();
    }
};

struct OfflineDynamicConnectivity{
    using edge=pair<int, int>;
    int n, Q, sz;
    UnionFindUndo uf;
    vector<vector<edge>> seg;
    map<edge, int> appear;
    vector<pair<pair<int, int>, edge>> pend;

    OfflineDynamicConnectivity(int n, int Q):uf(n), n(n), Q(Q){
        sz=1;
        while(sz<Q) sz<<=1;
        seg.resize(2*sz-1);
    }
    void insert(int t, int a, int b){
        if(a>b) swap(a, b);
        appear[{a, b}]=t;
    }
    void erase(int t, int a, int b){
        if(a>b) swap(a, b);
        auto itr=appear.find({a, b});
        pend.push_back({{itr->second, t}, {a, b}});
        appear.erase(itr);
    }
    void add(int a, int b, const edge &e, int k, int l, int r){
        if(r<=a || b<=l) return;
        if(a<=l && r<=b){
            seg[k].push_back(e);
            return;
        }
        add(a, b, e, 2*k+1, l, (l+r)/2);
        add(a, b, e, 2*k+2, (l+r)/2, r);
    }
    void add(int a, int b, const edge &e){
        add(a, b, e, 0, 0, sz);
    }
    void build(){
        for(auto p:appear){
            add(p.second, Q, p.first);
        }
        for(auto p:pend){
            add(p.first.first, p.first.second, p.second);
        }
    }
    void run(const function<void(int)> &f, int k=0){
        for(auto e:seg[k]){
            uf.unite(e.first, e.second);
        }
        if(k<sz-1){
            run(f, 2*k+1);
            run(f, 2*k+2);
        }else if(k-sz+1<Q){
            f(k-sz+1);
        }
        for(auto e:seg[k]){
            uf.undo();
        }
    }
};
int main()
{
    int n, k;
    cin>>n>>k;
    int t[40040], u[40040], v[40040];
    OfflineDynamicConnectivity odc(n, k);
    for(int i=0; i<k; i++){
        cin>>t[i]>>u[i]>>v[i];
        if(t[i]==1) odc.insert(i, u[i], v[i]);
        else if(t[i]==2) odc.erase(i, u[i], v[i]);
    }
    odc.build();
    odc.run([&](int k){
        if(t[k]==3){
            if(odc.uf.same(u[k], v[k])) cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    });
    return 0;
}


Measured memory_bytes: 12796