#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <algorithm>
#include <set>
#include <map>
using namespace std;
const int MaxL = 16, N = 5005;
char buf[1 << 20], *p1, *p2;
#define GC (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? 0 : *p1 ++)
inline int _R() {
int d = 0; bool ty = 1; char t;
while (t = GC, (t < '0' || t > '9') && t != '-');
t == '-' ? (ty = 0) : (d = t - '0');
while (t = GC, t >= '0' && t <= '9') d = (d << 3) + (d << 1) + t - '0';
return ty ? d : -d;
}
inline int RD() {
static int seed = 20010916;
return seed =( (seed ^ 21323123)* 998244353 + 1234321237) & 0x7fffffff;
}
int n, m;
struct Graph {
set<int> mt[N];
void add(int x, int y) {
mt[x].insert(y);
mt[y].insert(x);
}
void del(int x, int y) {
mt[x].erase(y);
mt[y].erase(x);
}
bool find(int x, int y) {
return mt[x].count(y);
}
bool empty(int x) { return mt[x].empty(); }
} G[MaxL];
struct Node {
Node *Son[2], *fa;
bool hav;
int pid, tid, sz, w;
} pool[10000000], *bin[10000000], *null;
int tl;
void ninit() {
null = pool;
null -> Son[0] = null -> Son[1] = null -> fa = null;
for (int i = 1; i < N * MaxL; i ++) bin[i] = pool + i;
}
struct ETT {
Graph *G;
set<int> e[N];
map<int, int> mt[N];
Node *End[2000005], *id[N]; int tote;
Node* newnode(int id, int tid) {
Node *x = bin[++ tl];
x -> pid = id;
x -> tid = tid;
x -> w = RD();
x -> hav = id && !G -> empty(id);
x -> sz = 1;
x -> Son[0] = x -> Son[1] = x -> fa = null;
return x;
}
void delnode(Node *x) {
bin[tl --] = x;
}
void pushup(Node *x) {
x -> hav = (x -> pid && !G -> empty(x -> pid)) || x -> Son[0] -> hav || x -> Son[1] -> hav;
x -> sz = x -> Son[0] -> sz + x -> Son[1] -> sz + 1;
}
void split_up(Node *x, Node *&l, Node *&r) {
if (x -> fa == null) return;
Node *y = x -> fa; x -> fa = null;
if (y -> Son[0] == x) {
y -> Son[0] = r;
r -> fa = y;
pushup(y);
r = y;
}
else {
y -> Son[1] = l;
l -> fa = y;
pushup(y);
l = y;
}
split_up(y, l, r);
}
void split(Node *x, Node *&l, Node *&r) {
l = x -> Son[0], r = x -> Son[1];
x -> Son[0] = x -> Son[1] = l -> fa = r -> fa = null;
pushup(x);
split_up(x, l, r);
}
Node* merge(Node *l, Node *r) {
if (l == null || r == null) return l == null ? r : l;
if (RD()&16) {
l -> Son[1] = merge(l -> Son[1], r);
l -> Son[1] -> fa = l;
pushup(l);
return l;
}
else {
r -> Son[0] = merge(l, r -> Son[0]);
r -> Son[0] -> fa = r;
pushup(r);
return r;
}
}
void makeroot(Node *&x) {
if (x == null) return;
Node *l, *r;
split(x, l, r);
x = merge(merge(x, r), l);
}
Node* findrt(Node *x) {
while (x -> fa != null) x = x -> fa;
while (x -> Son[0] != null) x = x -> Son[0];
return x;
}
Node *findrt(int x) {
return findrt(id[x]);
}
void add(int u, int v, bool lev) {
Node *x = id[u], *y = id[v];
if (x != null && y != null && findrt(x) == findrt(y)) {
pushup(x), pushup(y);
while (x -> fa != null) x = x -> fa, pushup(x);
while (y -> fa != null) y = y -> fa, pushup(y);
return;
}
makeroot(x), makeroot(y);
End[++ tote] = newnode(x == null ? u : 0, u), mt[u][v] = tote;
End[++ tote] = newnode(y == null ? v : 0, v), mt[v][u] = tote;
if (lev) e[u].insert(v), e[v].insert(u);
merge(merge(merge(End[tote - 1], y), End[tote]), x);
if (x == null) id[u] = End[tote - 1];
if (y == null) id[v] = End[tote];
}
void getid(int x) {
if (mt[x].empty()) { id[x] = null; return; }
id[x] = End[mt[x].begin() -> second];
Node *l, *r;
split(id[x], l, r);
id[x] -> pid = x; pushup(id[x]);
merge(merge(id[x], r), l);
}
int del(int u, int v) {
int eid = mt[u][v];
mt[u].erase(v), mt[v].erase(u);
Node *x = End[eid], *y = End[eid ^ 1], *l, *r;
split(x, l, r);
merge(r, l);
split(y, l, r);
int t0 = l -> sz,
t1 = r -> sz;
if (x -> pid) getid(u);
if (y -> pid) getid(v);
delnode(x), delnode(y);
return t1 < t0 ? u : v;
}
void init(int x) {
tote = 1;
fill(id, id + n + 1, null);
G = :: G + x;
}
void _print(Node *p) {
if (p -> Son[0] != null) _print(p -> Son[0]);
printf("%d ", p -> tid);
if (p -> Son[1] != null) _print(p -> Son[1]);
}
void print(int x) {
Node *p = id[x];
if (p == null) return;
makeroot(p);
_print(p);
puts("");
}
} T[MaxL];
namespace D_Graph {
void addlevel(int level, Node *x) {
if (x == null || !x -> hav) return;
if (x -> pid) {
int u = x -> pid;
for (auto v : T[level].e[u]) if (u < v) {
G[level].del(u, v);
G[level + 1].add(u, v);
T[level + 1].add(u, v, 1);
}
T[level].e[u].clear();
}
addlevel(level, x -> Son[0]);
addlevel(level, x -> Son[1]);
T[level].pushup(x);
}
Node *xrt;
int X, Y;
bool findin_G(int level, int x) {
while (!G[level].empty(x)) {
int u = *G[level].mt[x].begin();
if (T[level].findrt(u) != xrt) {
X = x, Y = u;
return 1;
}
G[level].del(x, u);
G[level + 1].add(x, u);
T[level + 1].add(x, u, 0);
}
return 0;
}
bool findin_T(int level, Node *x) {
if (x == null || !x -> hav) return 0;
if (x -> pid) if (findin_G(level, x -> pid)) return 1;
if (findin_T(level, x -> Son[0])) return 1;
if (findin_T(level, x -> Son[1])) return 1;
x -> hav = 0;
return 0;
}
void find_replacement(int level, int x) {
Node *p = T[level].id[x];
xrt = p;
if (p == null) findin_G(level, x);
else T[level].makeroot(p), addlevel(level, p), findin_T(level, p);
}
void add(int x, int y) {
G[0].add(x, y);
T[0].add(x, y, 1);
}
void del(int x, int y) {
for (int i = MaxL - 1; i >= 0; i --) if (G[i].find(x, y)) {
G[i].del(x, y);
if (!T[i].mt[x].count(y)) return;
T[i].e[x].erase(y);
T[i].e[y].erase(x);
X = Y = 0;
for (int j = i; j >= 0; j --) {
int t = T[j].del(x, y);
if (!X) {
find_replacement(j, t);
if (X) T[j].add(X, Y, 1);
}
else T[j].add(X, Y, 0);
}
return;
}
}
bool isconnected(int x, int y) {
return T[0].id[x] != null && T[0].id[y] != null && T[0].findrt(x) == T[0].findrt(y);
}
}
int main() {
int i, opt, x, y, ans = 0;
n = _R(), m = _R();
ninit();
for (i = 0; i < MaxL; i ++) T[i].init(i);
for (i = 1; i <= m; i ++) {
opt = _R(), x = _R() ^ ans, y = _R() ^ ans;
assert(x && y);
if (opt == 0) D_Graph :: add(x, y);
else if (opt == 1) D_Graph :: del(x, y);
else {
ans = D_Graph :: isconnected(x, y);
puts(ans ? "Y" : "N");
ans = ans ? x : y;
}
}
}