#include using namespace std; #define rep(i, n) for (int i = 0; i < (n); ++i) struct Query { int type, a, b; }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector> edges(m); vector> to(n); rep(i, m) { int u, v; cin >> u >> v; --u, --v; edges[i] = { u, v }; to[u].push_back(v); } int q; cin >> q; vector queries(q); rep(i, q) { auto& [type, a, b] = queries[i]; cin >> type >> a >> b; --a, --b; if (type == 1) to[a].push_back(b); } rep(i, n) { auto& v = to[i]; sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); } vector> active(n); rep(i, n) active[i].assign(to[i].size(), false); auto get_index = [&](int a, int b) { return int(lower_bound(to[a].begin(), to[a].end(), b) - to[a].begin()); }; for (auto [u, v] : edges) { active[u][get_index(u, v)] = true; } const int B = max(1, int(sqrt(m + q)) + 1); vector heavy; vector heavy_id(n, -1); rep(i, n) { if ((int)to[i].size() > B) { heavy_id[i] = heavy.size(); heavy.push_back(i); } } int h = heavy.size(); vector locked_out(h, 0); vector locked(n, false); int locked_count = 0; for (auto [type, a, b] : queries) { if (type == 1) { int idx = get_index(a, b); bool& dummy = *reinterpret_cast(&active[a][idx]); // Toggle the edge. dummy = !dummy; if (heavy_id[a] != -1 && locked[b]) { locked_out[heavy_id[a]] += dummy ? 1 : -1; } } else { locked[a] = !locked[a]; locked_count += locked[a] ? 1 : -1; int delta = locked[a] ? 1 : -1; for (int i = 0; i < h; ++i) { int u = heavy[i]; int idx = get_index(u, a); if (idx < (int)to[u].size() && to[u][idx] == a && active[u][idx]) { locked_out[i] += delta; } } } int cnt = 0; if (heavy_id[a] != -1) { cnt = locked_out[heavy_id[a]]; } else { rep(i, to[a].size()) { if (active[a][i] && locked[to[a][i]]) ++cnt; } } int ans = n - locked_count + cnt - !locked[a]; cout << ans << '\n'; } }