题解:P17235 [Algo Beat Contest 017 D] 图博弈
思路
根据游戏规则,第
因此,若要满足最终恰好剩
具体而言:
-
图总节点数为
n ,长度为n 的游走需经过n+1 个顶点。由鸽巢原理,该游走必定存在重复节点,即其中必然包含环。若k 所在的连通块无环,则必定无解。 -
为保证游走唯一,逆向移动时不能出现分岔,即在这条游走对应的结构中,每一步都只能有唯一的前驱。
综上,满足条件的局部骨架只能由一个环及一条通向
考虑在图中找出一个包含
具体而言:
可将输入视为无向图。从
接着记录闭合边的两个端点,先将深度较大的节点向上回溯至同一深度,再同步向上回溯至最近公共祖先。在此过程中,将经过的树边定向,从而构成一个有向环。随后从 LCA 沿父亲指针一路回溯至
最后将所有骨架节点压入队列,进行多源 BFS,将所有未定向边沿 BFS 的扩展方向定向,以防止外围节点重新连入骨架而产生额外棋子。
时间复杂度
单源 BFS 的时间复杂度为
代码实现
#include <bits/stdc++.h>
using namespace std;
const int N = 3005, M = 6005;
vector<pair<int, int>> g[N];
int dis[N], fa[N], fe[N];
int U[M], V[M], ans[M], vise[M];
void dir(int id, int u, int v)
{
ans[id] = (U[id] == u) ? 0 : 1;
vise[id] = 1;
}
void solve()
{
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
{
g[i].clear();
dis[i] = -1;
}
for (int i = 1; i <= m; i++)
{
cin >> U[i] >> V[i];
g[U[i]].push_back({V[i], i});
g[V[i]].push_back({U[i], i});
vise[i] = 0;
ans[i] = 0;
}
// 寻找环的闭合边
queue<int> q;
q.push(k);
dis[k] = fa[k] = fe[k] = 0;
int cu = 0, cv = 0, ce = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
for (auto tmp : g[u])
{
int v = tmp.first, id = tmp.second;
if (id == fe[u]) continue;
if (dis[v] == -1)
{
dis[v] = dis[u] + 1;
fa[v] = u;
fe[v] = id;
q.push(v);
}
else if (!ce)
{
cu = u;
cv = v;
ce = id;
}
}
}
if (!ce)
{
cout << "No\n";
return;
}
// 求 LCA
dir(ce, cu, cv);
int u = cu, v = cv;
while (dis[u] > dis[v])
{
dir(fe[u], fa[u], u);
u = fa[u];
}
while (dis[v] > dis[u])
{
dir(fe[v], v, fa[v]);
v = fa[v];
}
while (u != v)
{
dir(fe[u], fa[u], u);
dir(fe[v], v, fa[v]);
u = fa[u];
v = fa[v];
}
while (u != k)
{
dir(fe[u], u, fa[u]);
u = fa[u];
}
// 非骨架边隔离
for (int i = 1; i <= n; i++) dis[i] = -1;
while (!q.empty()) q.pop();
dis[k] = 0;
q.push(k);
for (int i = 1; i <= m; i++)
{
if (vise[i])
{
if (dis[U[i]] == -1)
{
dis[U[i]] = 0;
q.push(U[i]);
}
if (dis[V[i]] == -1)
{
dis[V[i]] = 0;
q.push(V[i]);
}
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
for (auto tmp : g[u])
{
int v = tmp.first, id = tmp.second;
if (!vise[id])
{
dir(id, u, v);
if (dis[v] == -1)
{
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
}
cout << "Yes\n";
for (int i = 1; i <= m; i++)
cout << ans[i];
cout << "\n";
}
int main()
{
cin.tie(0)->sync_with_stdio(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}