P9189 题解
Solution
能隐约感觉到,应该是通过某种方式把所有的钥匙都收集起来,然后通过某种方式把它们放到要放的地方。
Subtask 1
问你是否能收集到所有钥匙。
这个可以通过搜索的方式解决。你在第一个房间会有一个钥匙,然后能进入所有的和这个钥匙颜色相同的相邻的房间,获得新的钥匙。
唯一麻烦的事情是,如果
解决方法是,开一个额外的 set 记录每种颜色有哪些点是等待钥匙支援后能立刻进入的。
复杂度
Subtask 2
假设你有了所有的钥匙,如何重新分配。
考虑把目标当做现有的钥匙,重新进行 Subtask 1 的工作。我们惊奇的发现,如果把拿钥匙看做放钥匙,那么倒着走这个路线就是合法的。
所以可以类似 Subtask 1 的方法进行判断。
唯一不同的是,如果这个点的钥匙和颜色相同,在 Subtask 1 中如果没有持有相同颜色的钥匙就进不去,但是 Subtask 2 中进去就是合理的。
还有一个小细节,这个图可能不连通。如果 Subtask 1 中一个点不能到达,必须要求它的起始状态和目标状态相同,而且在 Subtask 2 中不能到达它。
代码:
#include<bits/stdc++.h>
#define ffor(i,a,b) for(int i=(a);i<=(b);i++)
#define roff(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int MAXN=2e5+10;
int T,n,m,c[MAXN],k[MAXN],s[MAXN],f[MAXN],tag[MAXN],flg[MAXN],extra[MAXN];
//flg:Whether or not I own the key colored i
//tag:Whether or not I have visited the point(the one on the graph)
vector<int> G[MAXN];
set<int> q[MAXN];
int check(int op) {
ffor(i,1,n) q[i].clear(),flg[i]=0,tag[i]=0;
queue<int> d; d.push(1); flg[k[1]]=1;
while(!d.empty()) {
int u=d.front(); d.pop();
if(tag[u]) continue;
tag[u]=1; if(!flg[k[u]]) {
flg[k[u]]=1;
for(auto v:q[k[u]]) if(!tag[v]) d.push(v);
q[k[u]].clear();
}
for(auto v:G[u]) {
if(tag[v]) continue;
if(extra[v]) continue;
if(op&&c[v]==k[v]) {d.push(v);continue;}
if(flg[c[v]]) d.push(v);
else q[c[v]].insert(v);
}
}
if(!op) {
ffor(i,1,n) if(!tag[i]) extra[i]=1;
}
ffor(i,1,n) if(!tag[i]&&s[i]!=f[i]) return 0;
return 1;
}
void work(void) {
cin>>n>>m;
ffor(i,1,n) extra[i]=0;
ffor(i,1,n) G[i].clear(),cin>>c[i];
ffor(i,1,n) cin>>s[i];
ffor(i,1,n) cin>>f[i];
ffor(i,1,m) {
int u,v; cin>>u>>v;
G[u].push_back(v),G[v].push_back(u);
}
int flg=1;
ffor(i,1,n) k[i]=s[i];
flg&=check(0);
ffor(i,1,n) k[i]=f[i];
flg&=check(1);
if(flg) cout<<"YES\n"; else cout<<"NO\n";
return ;
}
int main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>T; while(T--) work();
return 0;
}