#[allow(unused_imports)]
use std::io::{stdin, Read, StdinLock};
#[allow(unused_imports)]
use std::cmp::{max, min, Ordering};
#[allow(unused_imports)]
use std::str::FromStr;
#[allow(unused_imports)]
use std::collections::{HashSet, HashMap, BinaryHeap, VecDeque};
#[allow(unused_imports)]
use std::vec::Vec;

struct Scanner<'a> {
    cin: StdinLock<'a>,
}

#[allow(dead_code)]
impl<'a> Scanner<'a> {
    fn new(cin: StdinLock<'a>) -> Scanner<'a> {
        Scanner {cin: cin}
    }
    fn read1<T: FromStr>(&mut self) -> Option<T> {
        let token = self.cin.by_ref().bytes().map(|c| c.unwrap() as char)
                    .skip_while(|c| c.is_whitespace())
                    .take_while(|c| !c.is_whitespace())
                    .collect::<String>();
        token.parse::<T>().ok()
    }
    fn read<T: FromStr>(&mut self) -> T {
        self.read1().unwrap()
    }
}


fn main() {
    let cin = stdin();
    let cin = cin.lock();
    let mut sc = Scanner::new(cin);

    let n: usize = sc.read();
    let k: usize = sc.read();
    let mut t: Vec<usize> = vec![0; k];
    let mut u: Vec<usize> = vec![0; k];
    let mut v: Vec<usize> = vec![0; k];
    for i in 0..k {
        t[i] = sc.read();
        u[i] = sc.read();
        v[i] = sc.read();
        if u[i] > v[i] {
            std::mem::swap(&mut u[i], &mut v[i]);
        }
    }
    let t = t;
    let u = u;
    let v = v;

    let mut g: Vec<HashSet<usize>> = vec![HashSet::new(); n];
    let block = 300;
    for b in 0..(k + block - 1) / block {
        let lb = b * block;
        let ub = min((b + 1) * block, k);
        let mut appear: HashSet<usize> = HashSet::new();
        for i in lb..ub {
            appear.insert(u[i]);
            appear.insert(v[i]);
        }
        const INF: usize = 1000_000;
        let mut id: Vec<usize> = vec![INF; n];
        let mut m = appear.len();
        for v in 0..n {
            if appear.contains(&v) || id[v] != INF {
                continue;
            }
            let mut que: VecDeque<usize> = VecDeque::new();
            que.push_back(v);
            id[v] = m;
            while let Some(v) = que.pop_front() {
                for &to in &g[v] {
                    if id[to] == INF && !appear.contains(&to) {
                        id[to] = m;
                        que.push_back(to);
                    }
                }
            }
            m += 1;
        }
        for (j, v) in appear.iter().enumerate() {
            id[*v] = j;
        }

        let mut g2: Vec<HashSet<usize>> = vec![HashSet::new(); m];
        for v in 0..n {
            if id[v] == INF {
                continue;
            }
            for &to in &g[v] {
                if id[to] != INF && id[to] != id[v] {
                    g2[id[v]].insert(id[to]);
                    g2[id[to]].insert(id[v]);
                }
            }
        }

        for i in lb..ub {
            let u = u[i];
            let v = v[i];
            if t[i] == 1 {
                g2[id[u]].insert(id[v]);
                g2[id[v]].insert(id[u]);
                g[u].insert(v);
                g[v].insert(u);
            } else if t[i] == 2 {
                g2[id[u]].remove(&id[v]);
                g2[id[v]].remove(&id[u]);
                g[u].remove(&v);
                g[v].remove(&u);
            } else if t[i] == 3 {
                let mut que: VecDeque<usize> = VecDeque::new();
                let mut visited: Vec<bool> = vec![false; m];
                visited[id[u]] = true;
                que.push_back(id[u]);
                while let Some(now) = que.pop_back() {
                    for &to in &g2[now] {
                        if !visited[to] {
                            visited[to] = true;
                            que.push_back(to);
                        }
                    }
                }
                if visited[id[v]] {
                    println!("YES");
                } else {
                    println!("NO");
                }
            } else {
                unreachable!();
            }
        }
    }
}


Measured memory_bytes: 23936