#include<bits/stdc++.h> usingnamespace std; #define N 100010 int n, m; int Q; int u, v; int fa[N]; //并查集 voidini(){ for (int i = 1; i <= n; ++i) fa[i] = i; } intgetfa(int v){ return v == fa[v] ? v : fa[v] = getfa(fa[v]); } set<int> g[N], r[N]; //用set维护图,方便去除重边,可以使用更高效的方法。 //g: 正向图,r: 反向边的图 intmain(){ scanf("%d%d", &n, &m); ini(); for (int i = 1; i <= m; ++i) { scanf("%d%d", &u, &v); g[u].emplace(v); r[v].emplace(u); } queue<int> q; //队列,拓扑排序和后面合并可以用一个队列,节省一点 for (int i = 1; i <= n; ++i) { if (g[i].size() == 0) q.emplace(i); } while (!q.empty()) { //去除走不到环里的点 int u = q.front(); q.pop(); fa[u] = 0; for (auto i : r[u]) { g[i].erase(u); if (!g[i].size()) q.emplace(i); } } for (int i = 1; i <= n; ++i) { //将出度为1的点加入队列 if (g[i].size() == 1) q.emplace(i); } while (!q.empty()) { //合并所有在一个必经关系中的点 int u = q.front(); q.pop(); int v = *g[u].begin(); u = getfa(u), v = getfa(v); if (u == v) continue; if (r[u].size() > r[v].size()) swap(u, v);//启发式合并 for (auto i : r[u]) { //这里只用合并入的边,因为出的边更改后也会找到根继续合并入的边 g[i].erase(u); g[i].emplace(v); r[v].emplace(i); if (g[i].size() == 1) q.emplace(i); //出度为1加入队列 } fa[u] = v; } scanf("%d", &Q); while (Q--) { scanf("%d%d", &u, &v); u = getfa(u), v = getfa(v); if (!u || !v || u == v) { //询问,如果有点走不到环或在一个关系中🧠必胜 putchar('B'); } else { putchar('H'); } } return0; }