题解
题目要求在当前城市被攻陷之后,有些城市会导致无法连通时发出红色警报。逆向思考,可以将删除操作改为添加。
如果添加当前点并且添加当前点连接的原有边后,导致原来两个不联通的部分连在一起则这个时候就是红色警报。
这个连接操作并且检测是否有两个不同的联通分量合并在一起,可以使用并查集压缩路径O(1)完成。
标记删除的点,逆向添加每个点,注意连接边时需要注意判断当前点是否存在。
可以先和一个已存在的点建立连接在和其他的点连接并判断是否合并为同一个联通分量。总复杂度O(n+m)。
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 510;
const int M = 5e3 + 10;
int los[M], vex[N]; //los攻陷顺序
bool red[M], vis[N]; //是否红色警报 是否被攻陷
vector<int> e[N];
int find(int x)
{
return vex[x] == x ? x : vex[x] = find(vex[x]);
}
bool join(int x, int y)
{
x = find(x), y = find(y);
if (x != y)
{
vex[x] = y;
return 1;
}
return 0;
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int n, m, p;
cin >> n >> m;
for (int i = 0; i < n; ++i)
vex[i] = i;
for (int i = 0; i < m; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
cin >> p;
for (int i = 1; i <= p; ++i)
scanf("%d", &los[i]), vis[los[i]] = 1;
for (int i = 0; i < n; ++i)
if (!vis[i])
for (int j : e[i])
if (!vis[j])
join(i, j); //将最终都没被攻陷的城市join一起
for (int i = p; i >= 1; --i)
{
int k = los[i]; //倒序攻陷点
int j = 0;
while (j < e[k].size() && vis[e[k][j]]) //找到第一个还存在的节点
j++;
if (j < e[k].size())
{
join(k, e[k][j++]); //先将第一个加入集合
for (; j < e[k].size(); ++j)
if (!vis[e[k][j]] && join(k, e[k][j])) //有其它存在的节点和现在的集合不一样
red[k] = 1;
}
vis[k] = 0;
}
for (int i = 1; i <= p; ++i)
{
if (red[los[i]])
printf("Red Alert: City %d is lost!\n", los[i]);
else
printf("City %d is lost.\n", los[i]);
}
if (p == n)
printf("Game Over.\n");
return 0;
}