module Graphical
	require 'set'

	def reachable_from(s, max_depth = nil)
		reached = Set[s]
		queue = [s]
		depth = 0
		loop {
			break if max_depth && depth >= max_depth
			next_queue = []
			until queue.empty?
				u = queue.shift
				each_out_connection_of(u){|v, _|
					next if reached.include?(v)
					reached << v
					next_queue << v
				}
			end
			break if next_queue.empty?
			queue = next_queue
			depth += 1
		}
		return reached
	end

	def bfs(start, goal)
		reached = Set[start]
		queue = [start]
		loop {
			next_queue = []
			until queue.empty?
				u = queue.shift
				return true if u == goal
				u.edges.each{|e|
					v = e.opposite[u]
					next if reached.include?(v)
					reached << v
					next_queue << v
				}
			end
			break if next_queue.empty?
			queue = next_queue
		}
		false
	end
end

class Graph
	include Graphical

	def initialize(*options)
		@options = Set[*options]
	end

	def add_node(property = {})
		Node.new(property)
	end

	def add_edge(u, v, property = {})
		e = Edge.new(u, v, property)
		u.edges << e
		v.edges << e
		e
	end
	
	def del_edge(e)
		u, v = e.opposite.keys
		u.del_edge(e)
		v.del_edge(e)
		e
	end

	class Node
		def initialize(property)
			@property = property
			@edges = Set.new
		end

		def del_edge(e)
			@edges.delete(e)
		end

		def to_s
			@property[:name] || super
		end

		def each_edge
			@edges.each{|e| 
				yield e
			}
		end

		attr_reader :property, :edges
	end

	class Edge
		def initialize(u, v, property)
			@opposite = {u => v, v => u}
			@property = property
		end
		attr_reader :opposite, :property

		def to_s
			@property[:name] || super
		end

		def length
			@property[:length] || 1
		end
	end

	def each_out_connection_of(u)
		u.each_edge{|e|
			yield(e.opposite[u], e.length)
		}
	end

	alias :each_in_connection_of :each_out_connection_of
end

n, m = gets.split.map &:to_i
g = Graph.new
nodes = (0..n-1).map{|i| g.add_node({:name => i}) }
edges = {}

b = Math.sqrt(m).ceil 

def g.each_out_connection_of(u)
	i = u.property[:name]
	super(u){|v, _|
		j = v.property[:name]
		yield(v, 1) unless $to_be_deleted[Set[i, j]]
	}
end


(1..m).each_slice(b){|a|
	break if a[-1] > 1000	
	#p a

	$to_be_deleted = {}
	queries = a.map {	
		q, i, j = gets.split.map &:to_i
		$to_be_deleted[[i, j]] = 0 if q == 2
		[q, i, j]
	}

	rep = (1..n).map{ nil }
	(0..n-1).each{|i|
		next if rep[i]
		u = nodes[i]
		g.reachable_from(u).each{|v|
			rep[v.property[:name]] = i
		}
	}

	c_g = Graph.new
	c_nodes = (1..n).map{ c_g.add_node }

	hash = {}
	edges.each_key{|x|
		i, j = x / n, x % n	
		u, v = rep[i], rep[j]
		u, v = v, u if u > v
		unless hash[u*n+v] 
			c_g.add_edge(c_nodes[u], c_nodes[v])
			hash[u*n+v] = 0
		end
	}

	queries.each{|q, i, j|
		case q
		when 1
			e = g.add_edge(nodes[i], nodes[j])
			edges[i*n+j] = e
			c_g.add_edge(c_nodes[rep[i]], c_nodes[rep[j]])
		when 2
			e = edges[i*n+j]
			g.del_edge(e)
			edges.delete(i*n+j)
		when 3
			puts c_g.bfs(c_nodes[i], c_nodes[j]) ? 'YES' : 'NO'
		end
	}
}