#include #include #include #include

using namespace std;

typedef pair <int, int> epr; typedef set erbt;

struct seg { int s, z, l, r; seg *ls, *rs; }; struct edge { int t; edge *next; }; struct oper { int t, u, v, s; };

const int maxn = 30009; const int maxq = 40009; const int maxe = 100009;

int n, m, q, r[maxn]; int fa[maxn], d[maxn], sz[maxn], tc, fc[maxn], ch[maxn], cl[maxn]; seg sbuf_arr[maxn « 2], *sbuf(sbuf_arr), *rt[maxn]; oper o[maxq]; erbt re; edge ebuf_arr[maxn « 1], *ebuf(ebuf_arr), *head[maxn];

#define midp(p) ((p->l+p->r)»1) inline seg* sgtBuild(int l, int r) { seg p(sbuf ++); p-> l = l, p-> r = r; p-> s = r - l; p-> z = 0; if (l + 1 < r) { p-> ls = sgtBuild(l, midp(p)); p-> rs = sgtBuild(midp(p), r); } return p; } inline void sgtChg(seg p, int l, int r) { if (p-> l == l && p-> r == r) p-> z = 1, p-> s = 0; else if (p-> z) return; else { if (r <= midp(p)) sgtChg(p-> ls, l, r); else if (l >= midp(p)) sgtChg(p-> rs, l, r); else sgtChg(p-> ls, l, midp(p)), sgtChg(p-> rs, midp(p), r); p-> s = p-> ls-> s + p-> rs-> s; } } inline int sgtQry(seg* p, int l, int r) { if (p-> l == l && p-> r == r) return p-> s; else if (p-> z) return 0; else if (r <= midp(p)) return sgtQry(p-> ls, l, r); else if (l >= midp(p)) return sgtQry(p-> rs, l, r); else return sgtQry(p-> ls, l, midp(p)) + sgtQry(p-> rs, midp(p), r); }

#define cpos(x) (d[x]-d[ch[fc[x]]]) void ersBridge(int u, int v) { while (fc[u] ^ fc[v]) if (d[ch[fc[u]]] > d[ch[fc[v]]]) { sgtChg(rt[fc[u]], 0, cpos(u) + 1); u = fa[ch[fc[u]]]; } else { sgtChg(rt[fc[v]], 0, cpos(v) + 1); v = fa[ch[fc[v]]]; } if (d[u] < d[v]) sgtChg(rt[fc[u]], cpos(u) + 1, cpos(v) + 1); else if (d[v] < d[u]) sgtChg(rt[fc[v]], cpos(v) + 1, cpos(u) + 1); }

int getRoot(int x) { register int p, q; for (p = r[x]; p ^ r[p]; p = r[p]); for (; x ^ p; q = r[x], r[x] = p, x = q); return x; }

inline void addEdge(int u, int v) { ebuf-> t = v; ebuf-> next = head[u]; head[u] = ebuf ++; }

void buildTree() { for (int i = 1; i <= n; ++ i) r[i] = i; static epr rme[maxe]; int tre(0); for (erbt :: iterator i = re. begin(); i != re. end(); ++ i) { int u(i-> first), v(i-> second); if (getRoot(u) != getRoot(v)) { addEdge(u, v); addEdge(v, u); r[getRoot(u)] = getRoot(v); } else rme[tre ++] = i; } static int q[maxn]; int hd(0), tl(1); memset(d, 0, sizeof(d)); d[q[0] = 1] = 1; while (hd < tl) { int p(q[hd ++]); for (edge e = head[p]; e; e = e-> next) if (!d[e-> t]) { fa[e-> t] = p; d[e-> t] = d[p] + 1; q[tl ++] = e-> t; } } tc = 0; for (int i = tl - 1; i >= 0; – i) { int p(q[i]), z(-1); sz[p] = 1; for (edge* e = head[p]; e; e = e-> next) if (e-> t != fa[p]) { sz[p] += sz[e-> t]; if (z == -1 || sz[e-> t] > sz[z]) z = e-> t; } if (z == -1) cl[fc[p] = ++ tc] = 1; else ++ cl[fc[p] = fc[z]]; ch[fc[p]] = p; } for (int i = 1; i <= tc; ++ i) rt[i] = sgtBuild(0, cl[i]); for (int i = 0; i < tre; ++ i) ersBridge(rme[i]. first, rme[i]. second); }

int main() { #ifndef ONLINE_JUDGE freopen(".in", “r”, stdin); #endif

scanf("%d%d", &n, &m);
for (int i = 0; i < m; ++ i) {
	int u, v;
	scanf("%d%d", &u, &v);
	if (u > v) 
		swap(u, v);
	re. insert(epr(u, v));
}
for (q = 0; 1; ++ q) {
	int opt;
	scanf("%d", &opt);
	if (opt == -1)
		break;
	o[q]. t = opt;
	scanf("%d%d", &o[q]. u, &o[q]. v);
	if (o[q]. u > o[q]. v)
		swap(o[q]. u, o[q]. v);
	if (opt == 0)
		re. erase(epr(o[q]. u, o[q]. v));
}
buildTree();
for (int i = q - 1; i >= 0; -- i)
	if (o[i]. t == 0) {
		ersBridge(o[i]. u, o[i]. v);
	}
	else if (o[i]. t == 1) {
		o[i]. s = 0;
		int u(o[i]. u), v(o[i]. v);
		while (fc[u] ^ fc[v])
			if (d[ch[fc[u]]] > d[ch[fc[v]]]) {
				o[i]. s += sgtQry(rt[fc[u]], 0, cpos(u) + 1);
				u = fa[ch[fc[u]]];
			}
			else {
				o[i]. s += sgtQry(rt[fc[v]], 0, cpos(v) + 1);
				v = fa[ch[fc[v]]];
			}
		if (d[u] < d[v])
			o[i]. s += sgtQry(rt[fc[u]], cpos(u) + 1, cpos(v) + 1);
		else if (d[v] < d[u])
			o[i]. s += sgtQry(rt[fc[v]], cpos(v) + 1, cpos(u) + 1);
	}
for (int i = 0; i < q; ++ i)
	if (o[i]. t == 1)
		printf("%d\n", o[i]. s);

}