题目大意
给定一个无向图,我们定义图中有四种点,一种是黑色的点表示终点,并且黑色的点始终是1号点,一种是红色的点,一种是灰色的点,最后一种就是没有颜色的点。
游戏规则:我们必须移动任意一个灰色的点到相邻的点上,如果灰色的点移动到了红色的点上,那么我们可以移动其他灰色的点继续上述操作(不能移动自己),否则游戏结束。如果我们最终能使灰色的点到达终点,那么输出YES,如果不能使灰色的的点到达终点,输出NO。一下图为例:
我们这么移动8->6,7->5,6->4, 5->6, 4->2, 6->4, 2->1游戏结束到达终点输出YES。
输入一个整数T,表示有T组测试数据(1≤T≤1e4)。测试用例由一个空字符串分隔
输入n和m表示有n个点m个边 (1≤n≤2e5 , 0≤m≤2e5),并且保证图一定联通,任何点之间都是可以到达的。
输入p和b表示有p个黑色的点和b个红色的点。其中(1≤p≤n,0≤b≤n )。也就是说可能没有红色的点。
输入p个两两不同的灰色的点,表示灰色的点所在的位置。
输入b个红色的点,表示红色点所在的位置(灰色点可以和红色点重合,并且一个点上可以有多个灰色点,如果b为0则这一行为空)。
输入m行u,v表示u和v之间有一条边。
可以保证所有测试用例的n之和不超过2e5。同样地,保证所有输入数据集的m之和不超过2e5
解题思路:
如果有灰色的点在终点直接输出YES,或者终点附近有灰色的点直接输出YES。
我们发现一个性质,那就是如果一个灰色的点能走到红色的点,并且这个红色的点周围也有红色的点,那么我们就可以一直在这两个红色的点之间一直走下去。所以我们把红色的点分为两类:
1.可以走无限步数的红色的点。
2.只能走一步的红色的点。
我们从终点出发找到所有能到达的灰色的点,并将这些点的编号和离终点的距离丢进一个vector<pair<int, int>> use;数组里面。
如果use为空那么一定无解。
我们把use内的第一个元素的编号定义为st,第一个元素到终点的距离定义为sum。我们从其他所有灰色点出发,如果这些灰色的点能走出第一种红色的点那么输出YES,如果这些灰色的点能走的第二种点的数量大于等于sum-1,那么也输出YES。但是我们第一个元素不一定就是能走到终点的点,因为我们可能第一个元素能走出第一种点,那么如果存在第二个元素也可以直接输出YES。
#include <bits/stdc++.h>
const int N = 2e5 + 10, M = N << 2;
int n, m;
typedef long long ll;
const int INF = 0x3f3f3f3f;
typedef std::pair<int, int> PII;
int h[N], ne[N << 1], e[N << 1], idx;
inline void add(int a, int b) {
ne[idx] = h[a], e[idx] = b, h[a] = idx ++;
}
inline void slove() {
std::cin >> n >> m;
std::vector<bool> grey(n, 0), red(n, 0);//i号点是不是灰色点或者红色点
idx = 0;
for (int i = 0; i < n; i ++) h[i] = -1;
int p, b;
std::cin >> p >> b;
std::vector<int> point;//存储所有灰色点编号
for (int i = 1; i <= p; i ++) {
int x;
std::cin >> x;
x --;
grey[x] = true;//给灰色点打标记
point.push_back(x);
}
for (int i = 1; i <= b; i ++) {
int x;
std::cin >> x;
x --;
red[x] = true;//给红色点打标记
}
while (m --) {
int a, b;
std::cin >> a >> b;
a --, b --;
add(a, b);
add(b, a);
}
std::vector<bool> good(n, 0);//本身是红色周围也有红色,那么到达这个点可以一直走下去。
for (int i = 0; i < n; i ++) {//找到本身是红色邻边也为红色的点
if (red[i]) {
bool f = false;
for (int j = h[i]; ~j; j = ne[j]) {
int k = e[j];
if (red[k]) {
f = true;
break;
}
}
if (f) good[i] = true;
}
}
if (grey[0]) {//如果灰色点在终点直接输出YES
std::cout << "YES" << '\n';
return ;
}
std::vector<PII> use;//记录能到达终点的灰色点的编号和到终点的距离
int st = -1, sum = 0;//记录离终点最近的点的编号和距离
auto bfs = [&]() -> void {
std::queue<PII> q;
std::vector<bool> vis(n);
q.push({0, 0});
vis[0] = true;
while (!q.empty()) {
PII u = q.front();
q.pop();
for (int i = h[u.first]; ~i; i = ne[i]) {
int j = e[i];
if (vis[j]) continue;
if (grey[j]) {
if (st == -1) {
st = j;
sum = u.second + 1;
}
use.push_back({st, u.second + 1});
}
if (red[j]) {
q.push({j, u.second + 1});
vis[j] = true;
}
}
}
};
bfs();
if (st == -1) {//如果没有灰色点可以到达终点直接输出NO即可
std::cout << "NO" << '\n';
return ;
}
bool ok = false;//ok表示是否有一个灰色的可以无限走(即能走到good的点上)
int step = 0;
for (auto &u: point) {
if (u != st) {//判断其他点是能走的步数。
bool f = false;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (good[j]) {
ok = true;
break;
}
if (red[j]) f = true;
}
if (f) step ++;
} else {//如果离终点最近的点可以无限走,并且还有其他可以到达终点的点直接输出YES
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (ok && use.size() > 1) {
std::cout << "YES" << '\n';
return ;
}
}
}
if (ok) break;
}
if (ok || sum == 1) std::cout << "YES" << '\n';
else if (step >= sum - 1) std::cout << "YES" << '\n';
else std::cout << "NO" << '\n';
}
int main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int _ = 1;
std::cin >> _;
while (_ --) slove();
return 0;
}
- Codeforces Tokens Round Graph 847codeforces tokens round graph 题解codeforces div3 847 codeforces 1835f graph good codeforces transitive 1900e graph educational codeforces round rated codeforces round 911 div codeforces round 864 div codeforces round 887 div codeforces round 863 div codeforces round 913 div