【JZOJ4805】跟踪【dfs】

版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/91044756

题目大意:

题目链接:https://jzoj.net/senior/#main/show/4805
在这里插入图片描述


思路:

由于是一棵树,那么无论从 s s 点要走到哪个点,陌生人到达那个点的路径都是唯一的。
所以可以枚举在哪个点相遇。
但是枚举的点也是有要求的。如果在到达这个点之前石神就被抓到了,那么这个点就不可能到达了。
所以可以采用 d f s dfs 序来枚举。如果在这个点被抓到了,那么就直接返回就可以了。
先求出 s , q , p s,q,p 三个点到所有点的距离(设为 d i s [ 1 / 2 / 3 ] [ x ] dis[1/2/3][x] ),然后按 d f s dfs 序枚举点。默认当石神到达这个点之后就不再移动。
陌生人移动到这个点的时间是 t = m i n ( d i s [ 2 ] [ x ] , d i s [ 3 ] [ x ] ) t=min(dis[2][x],dis[3][x]) ,在这段时间内,石神移动、等待了共 t + 1 2 \lfloor \frac{t+1}{2}\rfloor 单位时间。那么总时间就是 a n s x = t + t + 1 2 ans_x=t+\lfloor \frac{t+1}{2}\rfloor
如何判断返回呢?
如果石神移动到点 x x 的时间 × 2 \times 2 (因为每一回合陌生人移动2次,而石神移动1次)大于陌生人移动到 x x 的时间,那么说明在之前陌生人就可以抓到石神,直接返回就可以了。
答案就是 m a x ( a n s x ) max(ans_x)
时间复杂度 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;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/91044756
今日推荐