题解 P2189 【小Z的传感器】
思路
看前后两次顺序是否正确,其实就是判断能否通过未打上标记的节点相互到达 。本质上就是判断两个节点是否在同一个连通块里。我们想到使用并查集来维护。
我们可以先将所有有返回信息的节点打上标记, 然后按照顺序遍历依次取消标记,同时判断前后两个节点是否可以通过未打上标记的节点相互到达,如果结果为 false, 则输出 "No"。如果遍历完所有的节点依然合法,就输出 "Yes"。
上代码~
#include <cstdio>
#include <cstring>
using namespace std;
int n, m, k, q;
inline char nc () {static char buf[1 << 21], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread (buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;}
inline int read () {
register int x(0),f(1); char ch = nc ();
while (ch > '9' || ch < '0') {if (ch == '-') f = ~f +1; ch = nc ();}
while (ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = nc ();}
return x * f;
}
int fath[200003];
int find (int now) {
if (!(fath[now] ^ now)) return now;
return fath[now] = find (fath[now]);
}
int seq[200003];
bool vis[200003];
int head[200003], ent;
struct edge {
int from, to, next;
}e[400003];
inline void add (int u, int v) {
e[++ent] = (edge){u, v, head[u]};
head[u] = ent;
}
int main () {
n = read (), m = read (), k = read (), q = read ();
for (register int i(1); i <= m; ++i) {
int u = read (), v = read ();
add (u, v), add (v, u);
}
for (register int h(1); h <= q; ++h) {
memset (vis, true, sizeof(vis)); //清空标记
for (register int i(1); i <= k; ++i) seq[i] = read (), vis[seq[i]] = false;
vis[seq[1]] = true; //第一个有返回值的房间无论是什么,都一定是合理的
for (register int i(1); i <= n; ++i) fath[i] = i; //初始化
for (register int i(1); i <= n; ++i) {
if (vis[i]) {
for (register int j(head[i]); j; j = e[j].next) { //将未打上标记的点合并
int v = e[j].to;
if (vis[v]) {
if (fath[i] ^ fath[v]) {
fath[find(v)] = find (i);
}
}
}
}
}
bool flag = 1;
for (register int i(2); i <= k; ++i) { //从第二个点开始顺序取消标记
vis[seq[i]] = true;
for (register int j(head[seq[i]]); j; j = e[j].next) {
int v = e[j].to;
if (vis[v]) {
fath[find(seq[i])] = find(v);
}
}
if (fath[find(seq[i])] ^ fath[find(seq[i-1])]) {
puts("No");
flag = 0;
break;
}
}
if (flag) puts("Yes");
}
}