Problem:
Problem D: Graph Construction

n 匹のうさぎがいて, 0 番からn − 1 番の番号がついた小屋に1 匹ずつ住んでいる.

あるとき, 秘密の組織によって地下通路の建設工事が行われる, という情報がうさぎたちのもとへ入った. 地下通路を使えば, うさぎたちは他のうさぎたちの小屋へ遊びに行けるようになって嬉しい.

通路は両方向に進むことができ, また通路同士は交わらない. 諸事情により, 一度建設された通路が破壊されてしまうこともある. 通路の建設や破壊の工事は1 本ずつ行われ, 各工事中はうさぎは自分の小屋に留まっているものとする.

うさぎたちは, 工事のいくつかの段階において, あるうさぎとあるうさぎが遊べるかどうかを事前に知りたい. うさぎたちは仲良しなので, 遊びに行くときに他のうさぎの小屋を自由に通ることができる. プログラミングが好きなうさぎたちは, 昔似たような問題を解いたので簡単だろうと思って挑戦し出したが, なかなか効率の良いプログラムが書けない. うさぎの代わりにこの問題を解け.

Input

入力の一行目にはn とk がスペース区切りで与えられる。2 ≤ n ≤ 40 000, 1 ≤ k ≤ 40 000

つづくk 行には工事情報と質問が合わせて, 時間順に与えられる.

“1 u v” — 小屋u とv を結ぶ通路が建設される. 小屋u とv を結ぶ通路がないときにのみ現れる.

“2 u v” — 小屋u とv を結ぶ通路が破壊される. 小屋u とv を結ぶ通路があるときにのみ現れる.

“3 u v” — 小屋u とv のうさぎが遊べるかどうか判定せよ.

0 ≤ u < v < n

Output

入力に現れる各質問について, 遊べるなら”YES”を, そうでないなら”NO”を1 行ずつ出力せよ.

Sample Input 1

4 10
1 0 1
1 0 2
3 1 2
2 0 1
1 2 3
3 0 1
1 0 1
2 0 2
1 1 3
3 0 2

Sample Output 1

YES
NO
YES

Solution in Rust (judge verdict: Time Limit Exceeded)

#[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;

#[allow(dead_code)]
const INF: i32 = 1000_000_000;
#[allow(dead_code)]
const INFLL: i64 = 1000_000_000_000_000_000;
#[allow(dead_code)]
const EPS: f64 = 1.0e-10;
#[allow(dead_code)]
const MOD: i32 = 1000_000_007;
#[allow(dead_code)]
const MODLL: i64 = 1000_000_007;

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()
    }
}


struct UnionFind {
    par: Vec<i32>,
}

impl UnionFind {
    #[allow(dead_code)]
    fn new(n: usize) -> UnionFind {
        UnionFind {
            par: vec![-1; n],
        }
    }

    #[allow(dead_code)]
    fn root(&mut self, x: usize) -> usize {
        if self.par[x] < 0 {
            x
        } else {
            let p = self.par[x] as usize;
            self.par[x] = self.root(p) as i32;
            self.par[x] as usize
        }
    }

    #[allow(dead_code)]
    fn unite(&mut self, x: usize, y: usize) -> bool {
        let x = self.root(x);
        let y = self.root(y);
        if x == y {
            false
        } else {
            if self.size(x) < self.size(y) {
                self.par[y] += self.par[x];
                self.par[x] = y as i32; // usize <-> i32
            } else {
                self.par[x] += self.par[y];
                self.par[y] = x as i32;
            }
            true
        }
    }

    #[allow(dead_code)]
    fn same(&mut self, x: usize, y: usize) -> bool {
        self.root(x) == self.root(y)
    }

    #[allow(dead_code)]
    fn size(&mut self, x: usize) -> usize {
        let r = self.root(x);
        (-self.par[r]) as usize
    }
}

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 {
                    g2[id[v]].insert(id[to]);
                }
            }
        }

        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!();
            }
        }
    }
}