题目大意:
有一棵
n
n
n个节点的树,其中Alice的船在
x
x
x节点,Bob有很多船,分别在点
y
1
,
y
2
,
.
.
.
,
y
n
y_1,y_2,...,y_n
y1,y2,...,yn,每个回合每个船都可以走向相邻的节点或者不走,可以多个船在同一个点。
求
x
=
1
,
2
,
.
.
.
,
n
x=1,2,...,n
x=1,2,...,n时,Alice在不会与Bob的船在同一点的情况下游戏最多可以进行多少个回合。
分析:
假设经过了
k
k
k个回合,Bob的船都不能到点
p
p
p,那么从
p
p
p向周围走
k
−
1
k-1
k−1步的点都可以至少进行
k
k
k个回合。
我们两次dfs处理出
f
[
x
]
f[x]
f[x]表示Bob的船走到
x
x
x至少要多少个回合,然后对
f
[
x
]
f[x]
f[x]从大到小覆盖出答案。
如果覆盖某一个节点还能走的步数小于等于前面覆盖更大的答案时还能走的步数,就可以退出。
因为能覆盖的步数以及权值都是不断减小的,所以这样暴力覆盖也是可以通过这题的。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
const int maxn=2e5+7;
using namespace std;
int n,m,cnt,x,y;
int a[maxn],ans[maxn],b[maxn],f[maxn],ls[maxn];
struct edge{
int y,next;
}g[maxn*2];
bool cmp(int x,int y)
{
return f[x]>f[y];
}
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
void dfs1(int x,int fa)
{
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
dfs1(y,x);
f[x]=min(f[x],f[y]+1);
}
}
void dfs2(int x,int fa)
{
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
f[y]=min(f[y],f[x]+1);
dfs2(y,x);
}
}
void solve(int x,int dep,int w)
{
if (dep<0) return;
if (b[x]>=dep) return;
else b[x]=dep;
ans[x]=max(ans[x],w);
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
solve(y,dep-1,w);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for (int i=1;i<=n;i++)
{
f[i]=0x3f3f3f3f;
a[i]=i;
b[i]=-1;
}
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%d",&x);
f[x]=0;
}
dfs1(1,0);
dfs2(1,0);
sort(a+1,a+n+1,cmp);
for (int i=1;i<=n;i++) solve(a[i],f[a[i]]-1,f[a[i]]);
for (int i=1;i<=n;i++) printf("%d ",ans[i]);
}