【JZOJ4805】跟踪【dfs】

题目大意:

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


思路:

由于是一棵树,那么无论从ss点要走到哪个点,陌生人到达那个点的路径都是唯一的。
所以可以枚举在哪个点相遇。
但是枚举的点也是有要求的。如果在到达这个点之前石神就被抓到了,那么这个点就不可能到达了。
所以可以采用dfsdfs序来枚举。如果在这个点被抓到了,那么就直接返回就可以了。
先求出s,q,ps,q,p三个点到所有点的距离(设为dis[1/2/3][x]dis[1/2/3][x]),然后按dfsdfs序枚举点。默认当石神到达这个点之后就不再移动。
陌生人移动到这个点的时间是t=min(dis[2][x],dis[3][x])t=min(dis[2][x],dis[3][x]),在这段时间内,石神移动、等待了共t+12lfloor frac{t+1}{2} floor单位时间。那么总时间就是ansx=t+t+12ans_x=t+lfloor frac{t+1}{2} floor
如何判断返回呢?
如果石神移动到点xx的时间×2 imes 2(因为每一回合陌生人移动2次,而石神移动1次)大于陌生人移动到xx的时间,那么说明在之前陌生人就可以抓到石神,直接返回就可以了。
答案就是max(ansx)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
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998143.html