Hoof and Brain P 题解
Hoof and Brain P 题解
模拟考考场上没干出来,赛后几分钟过了。
首先考虑一个简单情况,如果一个节点的出度为 \(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;
}

浙公网安备 33010602011771号