1 条题解
-
0
【形式化题意】
A 和 B 在一张有向图上进行博弈, 次询问指定两个点放置两个棋子,每轮 A 可以指定一个棋子,B 可以选择一条边移动这枚棋子;如果游戏会在有限次内结束,则 A 胜,否则 B 胜。
【解法】
如果存在一个棋子位于出度为 的点,则 A 可以指定这个节点,即为 A 必胜点。
如果棋子位于的节点的所有出边都指向 A 必胜点,则这个节点也为 A 必胜点。
形式化地,在反图上跑拓扑排序,反图上拓扑过程中入度为零的点都是 A 必胜点。
先做一次拓扑处理掉这些点,则剩下的点都至少可以到达一个环。
理论上来说,这时候可以随便走了,但是注意到两个棋子在任何时刻不允许处于同一个结点上,所以可以通过一个棋子阻挡另一个的方式,使 A 获胜。
当一个点出度 时,一定不可以挡住。则考虑出度为 的点,或者说考虑一条出度均为 的链。
如果两个棋子都在这条链上,A 反复指定“靠上”的那个棋子,B 没有其他选择,最终与另一个棋子撞上,使 A 获胜。
而我们注意到,在这条出度为 的链上,两个棋子具体放在哪里无所谓,所以我们可以把链上的点即所有 度点全部缩在一起,用并查集维护。
此时如果两个点中存在一个点被第一次拓扑删除,或者在缩完之后缩到了一个点,是 A 胜,否则是 B 胜。
用 set 维护出边,利用启发式合并思想合并两个节点,可以使复杂度有保障。
【code】
#include<bits/stdc++.h> using namespace std; const int nr = 1e5 + 10; int n, m, ft[nr]; set<int> g[nr], ex[nr]; bool del[nr]; int find(int x) { if (ft[x] == x) return x; return ft[x] = find(ft[x]); } signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n >> m; for (int i = 1; i <= n; i++) ft[i] = i; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; g[u].insert(v), ex[v].insert(u); } queue<int> q; for (int i = 1; i <= n; i++) if (!g[i].size()) q.push(i); while (!q.empty()) { int u = q.front(); q.pop(); del[u] = true; for (set<int>::iterator it = ex[u].begin(); it != ex[u].end(); it++) { g[*it].erase(u); if (!g[*it].size()) q.push(*it); } ex[u].clear(); } for (int i = 1; i <= n; i++) if (!del[i] && g[i].size() == 1) q.push(i); while (!q.empty()) { int u = q.front(); q.pop(); u = find(u); int v = *g[u].begin(); if (u == v || g[u].size() != 1) continue; g[u].erase(v), ex[v].erase(u); if (g[v].size() + ex[v].size() > g[u].size() + ex[u].size()) swap(u, v); ft[v] = u; bool cir = false; for (set<int>::iterator it = g[v].begin(); it != g[v].end(); it++) { if (*it == v) { cir = true; continue; } ex[*it].erase(v), ex[*it].insert(u); g[u].insert(*it); } for (set<int>::iterator it = ex[v].begin(); it != ex[v].end(); it++) { if (*it == v) { cir = true; continue; } g[*it].erase(v), g[*it].insert(u); ex[u].insert(*it); if (g[*it].size() == 1) q.push(*it); } if (cir) g[u].insert(u), ex[u].insert(u); g[v].clear(), ex[v].clear(); if (g[u].size() == 1) q.push(u); } int Q; cin >> Q; while (Q--) { int x, y; cin >> x >> y; cout << (del[find(x)] || del[find(y)] || find(x) == find(y) ? 'B' : 'H'); } cout << '\n'; return 0; }
- 1
信息
- ID
- 7649
- 时间
- 4000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- 递交数
- 10
- 已通过
- 5
- 上传者