题解:CF1611E2 Escape The Maze (hard version) (逃离迷宫(困难版))
Ruanqiming · · 题解
题解:CF1611E2 Escape The Maze (hard version) (逃离迷宫(困难版))
建议先阅读这篇题解,再阅读本篇。
题意
在一棵树上,有一个人 Vald 位于点
有
如果 Vald 在某一时刻和他的朋友处在同一节点,那么 Vald 无法获胜。
如果 Vald 到达了一个编号不是
求在给定的朋友中,最少选多少个朋友可以让 Vald 失败,或者所有朋友都无法让他失败。
思路
在上一篇题解中讲述了求所有朋友能否拦截他的方法,此处简略带过。
用多源 BFS 求出每个点到最近的朋友的距离,然后用 DFS 求出 Vald 到每个点的距离。
如果 Vald 到一个点的距离小于朋友到这个点的距离,那么他在这个点是安全的,判断每个叶子的路径上的所有点是不是安全,如果存在一个可以安全到达的编号不是
而在这道题中,我们换个视角,看朋友能不能胜利。
老样子,BFS 和 DFS 算出每个点是不是能抓到 Vald,如果一个点能抓到 Vald,那么他也无法到达以这个点为根的子树。
这就相当于,只要一个点能抓住,以这个点为根的子树就被这个点保护起来了,那么这个子树内的所有朋友都不需要了。
如果一个点没有被保护,且能抓到 Vald,那么这个点对应的朋友是有用的。
那么现在就有思路了,
如果一个点的父亲节点已经能抓到了,那么这个节点也无所谓了。
如果一个父亲节点不能抓到,且这个点能抓到,那么需要一个朋友蹲守在此处,此时答案增加
最后,判断是不是每一个编号不是
代码
:::success[AC code]
#include<bits/stdc++.h>
using namespace std;
int t,n,k,x[200010],b[200010],ans[200010],ans1[200010],f[200010],cnt;
vector<int> a[200010];
queue<int> que;
void bfs()//多源 BFS
{
while(!que.empty())
{
int now=que.front();
que.pop();
for(int i=0;i<a[now].size();i++)
{
if(!b[a[now][i]])
{
b[a[now][i]]=1;
ans[a[now][i]]=ans[now]+1;
que.push(a[now][i]);
}
}
}
}
void dfs(int x,int fa)
{
if(!f[fa] && ans[x]<=ans1[x])//如果父节点并没有被保护但这个节点能抓到 Vald 那么可行。
{
cnt++;
}
f[x]=f[fa] | ans[x]<=ans1[x];//更新这个点能否抓到 Vald。
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==fa)
{
continue;
}
ans1[a[x][i]]=ans1[x]+1;
dfs(a[x][i],x);
}
}
int main()
{
cin>>t;
while(t--)
{
cnt=0;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
a[i].clear();
b[i]=ans[i]=ans1[i]=f[i]=0;
}
for(int i=1;i<=k;i++)
{
cin>>x[i];
que.push(x[i]);
b[x[i]]=1;
}
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
a[x].push_back(y);
a[y].push_back(x);
}
bfs();
dfs(1,0);
bool f1=true;
for(int i=2;i<=n;i++)
{
if(a[i].size()==1 && !f[i])//如果有叶子结点没有覆盖到那就不行。
{
f1=false;
}
}
if(f1)
{
cout<<cnt<<endl;
}
else
{
cout<<-1<<endl;
}
}
return 0;
}//by Ruanqiming
::: 码风较丑,请见谅……
总结
相较于上一题来说,此题确实会难一点,要找到一个点不能到达,那么整个子树就无法到达的性质,上一题的思路不难,这题需要想想。