#include <bits/stdc++.h>
using namespace std;

struct undoable_dsu {
  struct dat { int p, sz; };
  int cc;
  vector<dat> t;
  vector<pair<int, dat>> hist;
  undoable_dsu(int n = 0) : cc(n), t(n, {-1, 1}) {}
  int root(int v) const {
    while (t[v].p != -1) v = t[v].p;
    return v;
  }
  void unite(int u, int v) {
    u = root(u), v = root(v);
    assert(u != v);
    --cc;
    if (t[u].sz < t[v].sz) swap(u, v);
    hist.emplace_back(u, t[u]);
    hist.emplace_back(v, t[v]);
    t[v].p = u;
    t[u].sz += t[v].sz;
  }
  void undo() {
    ++cc;
    for (int _ = 2; _--; ) {
      t[hist.back().first] = hist.back().second;
      hist.pop_back();
    }
  }
  bool same(int u, int v) const { return root(u) == root(v); }
  int size(int v) const { return t[root(v)].sz; }
};

enum struct query_type : char { ADD, REMOVE, SAME, SIZE, CC };
struct offline_dynamic_connectivity {
  struct seg { int l, r, u, v; };
  const int n;
  int time = 0;
  map<pair<int, int>, int> mp;
  vector<seg> s;
  vector<vector<tuple<query_type, int, int>>> q;
  offline_dynamic_connectivity(int _n) : n(_n) {}
  void add_query(query_type tp, int u = -1, int v = -1) {
    static bool last = false;
    if (u > v) swap(u, v);
    if (tp == query_type::ADD) {
      assert(mp.count({u, v}) == 0);
      mp[{u, v}] = time += exchange(last, false);
    } else if (tp == query_type::REMOVE) {
      assert(mp.count({u, v}) == 1);
      s.push_back({mp[{u, v}], time += exchange(last, false), u, v});
      mp.erase({u, v});
    } else {
      last = true;
      q.resize(time + 1);
      q[time].emplace_back(tp, u, v);
    }
  }
  vector<int> run() {
    ++time;
    for (auto e : mp)
      s.push_back({e.second, time, e.first.first, e.first.second});
    vector<vector<pair<int, int>>> t(2 * time);
    for (auto e : s)
      for (e.l += time, e.r += time; e.l < e.r; e.l >>= 1, e.r >>= 1) {
        if (e.l & 1) t[e.l++].emplace_back(e.u, e.v);
        if (e.r & 1) t[--e.r].emplace_back(e.u, e.v);
      }
    vector<int> res;
    undoable_dsu ud(n);
    auto dfs = [&](auto&& self, int i) -> void {
      int cnt = 0;
      for (auto e : t[i])
        if (not ud.same(e.first, e.second)) ud.unite(e.first, e.second), ++cnt;
      if (i < time) {
        self(self, 2 * i);
        self(self, 2 * i + 1);
      } else if (i - time < (int)q.size()) {
        for (auto e : q[i - time])
          if (get<0>(e) == query_type::SAME)
            res.push_back(ud.same(get<1>(e), get<2>(e)));
          else if (get<0>(e) == query_type::SIZE)
            res.push_back(ud.size(get<1>(e)));
          else res.push_back(ud.cc);
      }
      while (cnt--) ud.undo();
    };
    vector<int> rs;
    for (int l = time, r = 2 * time; l < r; l >>= 1, r >>= 1) {
      if (l & 1) dfs(dfs, l++);
      if (r & 1) rs.push_back(--r);
    }
    while (not rs.empty()) dfs(dfs, rs.back()), rs.pop_back();
    return res;
  }
};

int main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);
  int n, q;
  cin >> n >> q;
  offline_dynamic_connectivity d(n);
  while (q--) {
    int t, u, v;
    cin >> t >> u >> v;
    if (t == 1) {
      d.add_query(query_type::ADD, u, v);
    } else if (t == 2) {
      d.add_query(query_type::REMOVE, u, v);
    } else {
      d.add_query(query_type::SAME, u, v);
    }
  }
  for (int e : d.run()) {
    cout << (e ? "YES\n" : "NO\n");
  }
}



Measured memory_bytes: 7580