题解:P9189 [USACO23OPEN] Custodial Cleanup G
my_dream666 · · 题解
前言
很妙的题,属于做不倒系列,题目传送门。
做法
首先可以感觉到,先把所有钥匙收集起来,再把它们放到相应位置,这样一定更优。因为这样能达到的点尽可能多,也不会使后面放钥匙更劣。
现在考察能到达哪些点。不难发现可以用 BFS 实现,对于还没有相应钥匙的点,可以用桶存起来。如果有了该颜色的钥匙,就把桶清空并加入队列,将该钥匙打上可行标记。时间复杂度为
现在再考虑放钥匙操作。这时惊奇地发现,将
唯一不同在于,在第二种 BFS 中,如果没有该颜色的新钥匙,只要
最后只需要判断,第一次 BFS 能达到的点,第二次 BFS 是否也能到达即可。
还剩一些小细节:
-
第一次 BFS 不能到达的点,第二次 BFS 也不能进入。
-
第一次 BFS 不能达到的点,必须满足
S_i=F_i ,不然无解。
:::info[code]
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+10;
vector <int> e[N],tong[N];
int c[N],s[N],f[N];
bool vis[N],good[N],book[N];
queue <int> q;
void read(){}
template <typename T,typename ...Args>
void read(T &x,Args &...args)
{
x=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
read(args...);
}
inline void solve()
{
int n,m,x,y,now; read(n,m);
for(int i=1;i<=n;i++) e[i].clear(),tong[i].clear(),vis[i]=book[i]=good[i]=false;
for(int i=1;i<=n;i++) read(c[i]);
for(int i=1;i<=n;i++) read(s[i]);
for(int i=1;i<=n;i++) read(f[i]);
for(int i=1;i<=m;i++) read(x,y),e[x].emplace_back(y),e[y].emplace_back(x);
good[s[1]]=vis[1]=true; q.push(1);
while(!q.empty())
{
now=q.front(); q.pop(); good[s[now]]=true;
for(int i:tong[s[now]])
if(!vis[i]) q.push(i),vis[i]=true;
for(int i:e[now])
{
if(vis[i]) continue;
if(good[c[i]]) q.push(i),vis[i]=true;
else tong[c[i]].emplace_back(i);
}
}
for(int i=1;i<=n;i++)
if(!vis[i]&&s[i]!=f[i]) return puts("NO"),void();
for(int i=1;i<=n;i++) tong[i].clear(),good[i]=false;
good[f[1]]=book[1]=true; q.push(1);
while(!q.empty())
{
now=q.front(); q.pop(); good[f[now]]=true;
for(int i:tong[f[now]])
if(!book[i]) q.push(i),book[i]=true;
for(int i:e[now])
{
if(book[i]||!vis[i]) continue;
if(good[c[i]]||c[i]==f[i]) q.push(i),book[i]=true;
else tong[c[i]].emplace_back(i);
}
}
for(int i=1;i<=n;i++)
if(vis[i]&&!book[i]&&f[i]!=s[i]) return puts("NO"),void();
puts("YES");
}
int main()
{
int t; read(t);
while(t--) solve();
return 0;
}
:::