题目大意:
题目链接:https://jzoj.net/senior/#main/show/4805
思路:
由于是一棵树,那么无论从
s
s
s点要走到哪个点,陌生人到达那个点的路径都是唯一的。
所以可以枚举在哪个点相遇。
但是枚举的点也是有要求的。如果在到达这个点之前石神就被抓到了,那么这个点就不可能到达了。
所以可以采用
d
f
s
dfs
dfs序来枚举。如果在这个点被抓到了,那么就直接返回就可以了。
先求出
s
,
q
,
p
s,q,p
s,q,p三个点到所有点的距离(设为
d
i
s
[
1
/
2
/
3
]
[
x
]
dis[1/2/3][x]
dis[1/2/3][x]),然后按
d
f
s
dfs
dfs序枚举点。默认当石神到达这个点之后就不再移动。
陌生人移动到这个点的时间是
t
=
m
i
n
(
d
i
s
[
2
]
[
x
]
,
d
i
s
[
3
]
[
x
]
)
t=min(dis[2][x],dis[3][x])
t=min(dis[2][x],dis[3][x]),在这段时间内,石神移动、等待了共
⌊
t
+
1
2
⌋
\lfloor \frac{t+1}{2}\rfloor
⌊2t+1⌋单位时间。那么总时间就是
a
n
s
x
=
t
+
⌊
t
+
1
2
⌋
ans_x=t+\lfloor \frac{t+1}{2}\rfloor
ansx=t+⌊2t+1⌋。
如何判断返回呢?
如果石神移动到点
x
x
x的时间
×
2
\times 2
×2(因为每一回合陌生人移动2次,而石神移动1次)大于陌生人移动到
x
x
x的时间,那么说明在之前陌生人就可以抓到石神,直接返回就可以了。
答案就是
m
a
x
(
a
n
s
x
)
max(ans_x)
max(ansx)。
时间复杂度
O
(
n
)
O(n)
O(n)。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200010;
int n,s,q,p,tot,ans,dis[4][N],head[N];
struct edge
{
int to,next;
}e[N*2];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs(int x,int fa,int id)
{
for (int i=head[x];~i;i=e[i].next)
{
int y=e[i].to;
if (y!=fa)
{
dis[id][y]=dis[id][x]+1;
dfs(y,x,id);
}
}
}
void dfs_ans(int x,int fa)
{
if (dis[2][x]<dis[1][x]*2||dis[3][x]<dis[1][x]*2) return;
int ans_=min(dis[2][x]+(dis[2][x]+1+(!dis[2][x]))/2,dis[3][x]+(dis[3][x]+1+(!dis[3][x]))/2);
if (ans_>ans) ans=ans_;
for (int i=head[x];~i;i=e[i].next)
{
int y=e[i].to;
if (y!=fa) dfs_ans(y,x);
}
}
int main()
{
freopen("track.in","r",stdin);
freopen("track.out","w",stdout);
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&s,&q,&p);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs(s,0,1);
dfs(q,0,2);
dfs(p,0,3);
dfs_ans(s,0);
printf("%d\n",ans);
return 0;
}