Hoof and Brain P 题解
Link
模拟考考场上没干出来,赛后几分钟过了。
首先考虑一个简单情况,如果一个节点的出度为 \(0\) ,那么这个节点就是B的必胜点,我们可以将这个点删掉,重复这样的操作,使得每个节点都有出度。
除了上面这一种情况,B赢的情况就只有使得两个棋子重叠了。要使两个棋子重叠,就必须使其中一个棋子没有其他路可以走,也就是说这颗棋子一定可以通过一直走出度为 \(1\) 的点到达另一个棋子。
考虑启发式合并。对于出度为 \(1\) 的节点,我们可以将这个节点和他指向的节点合并,并继承其所有的入度。
最后统计答案时,如果节点不存在,B赢;如果两个节点在同一个节点,B赢;否则H赢。
代码 :
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1e5 + 5;
set<int> g[N], re[N];
int n, m, q;
int fa[N], siz[N];
queue<int> que;
queue<int> quw;
int find(int x) {if (fa[x] == x) return x;return fa[x] = find(fa[x]);
}
void merge(int x, int y) {x = find(x), y = find(y);if (x == y) return;if (re[x].size() > re[y].size()) swap(x, y);for (int u : re[x]) { // 继承入度re[y].insert(u);g[u].erase(x);g[u].insert(y);if (g[u].size() == 1) quw.push(u);}re[x].clear();fa[x] = y;
}
int main() {FASTIO;cin >> n >> m;for (int i = 1; i <= n; ++i) fa[i] = i; for (int i = 1, u, v; i <= m; ++i) {cin >> u >> v;g[u].insert(v);re[v].insert(u);}for (int i = 1; i <= n; ++i) {if (g[i].empty()) {que.push(i);}}while (que.size()) { // 删点int f = que.front();fa[f] = 0;que.pop();for (int v : re[f]) {g[v].erase(f);if (g[v].empty())que.push(v);}re[f].clear();}for (int i = 1; i <= n; ++i) {if (g[i].size() == 1) quw.push(i);}while (quw.size()) { // 合并int f = quw.front();quw.pop();if (g[f].size() == 1) {merge(f, *g[f].begin()); }}cin >> q;for (int i = 1; i <= q; ++i) {int x, y;cin >> x >> y;if (!fa[x] || !fa[y]) cout << "B";else if (find(x) == find(y)) cout << "B";else cout << "H";}return 0;
}