2020牛客暑期多校训练营The Flee Plan of Groundhog(树形DP)

The Flee Plan of Groundhog

题目描述

2020牛客暑期多校训练营The Flee Plan of Groundhog(树形DP)_第1张图片

样例

input:
7 2
1 2
2 5
5 7
5 6
3 6
3 4
output:
1

题目大意

土拨鼠和 O r a n g e Orange Orange同住在一棵树上。现在,土拨鼠去看望 O r a n g e Orange Orange,他从1号结点出发, O r a n g e Orange Orange住在 n n n号结点。土拨鼠速度为 1 m / s 1m/s 1m/s t t t秒之后, O r a n g e Orange Orange发现自己发烧了,为了传染给土拨鼠,他以 2 m / s 2m/s 2m/s的速度追赶以 1 m / s 1m/s 1m/s速度逃跑的土拨鼠。
现在给定 n , t n,t n,t和树,要求土拨鼠从走向 O r a n g e   t Orange\,t Oranget秒之后,他有多少时间可以存活。

分析

第一步肯定是找到土拨鼠 t t t秒之后的位置,这个很简单,求个深度和每个节点的父节点然后从 n n n开始向上跳即可。
接下来就是比较麻烦的求最长的那条路径。
我们第一想到的就是以现在土拨鼠的位置为根,除了 n n n那条链以外的链求最长的即可,你当然不会迎着 O r a n g e Orange Orange走过去。但是很快就发现十分的不对。
不如我们看下面这种情况:
2020牛客暑期多校训练营The Flee Plan of Groundhog(树形DP)_第2张图片
如果土拨鼠当前的位置在2,那么显然依照上面的策略,土拨鼠会在1号结点等死。但是还有个更优的策略就是向着 O r a n g e Orange Orange走一步,然后拐到 4567 4567 4567那条链上可以存活更久。

那么这个应该怎么搞呢??
我们不妨把 O r a n g e Orange Orange和土拨鼠到达每个节点的时间都标出来:
蓝色是土拨鼠,橙色是 O r a n g e Orange Orange
2020牛客暑期多校训练营The Flee Plan of Groundhog(树形DP)_第3张图片
相应地,土拨鼠肯定是不能到达橙色小于等于蓝色的点的因为这样, O r a n g e Orange Orange就可以直接抓住他了。因此对于每条链,一旦找到一个不合法的点,那么这个点的父节点就是土拨鼠向这条链能走的最远的路, a n s ans ans只要取这个点 O r a n g e Orange Orange所需要的时间就可以了。
那么如果整条链都是合法的,显然,取叶节点就可以了。
所以,3个 d f s dfs dfs爆搜,出答案。
暴力出奇迹

代码

#include
#define ll long long
using namespace std;
const int MAXN=1e5+100;
vector<int> vec[MAXN];//存边
int dep[MAXN],fa[MAXN],cs,blk[MAXN];
//blk Orange的时间 fa 父节点 dep 两个用处,见下面注释
int n,t,ans;
void dfs(int x,int depth,int fx)
{
	dep[x]=depth;
	for(int i=0;i<vec[x].size();i++){
		int s=vec[x][i];
		if(s==fx) continue;
		fa[s]=x;dfs(s,depth+1,x);
	}
}//预处理的dfs,先求出以1为根的向n的dep,然后方能求ts后现在的位置
//后面的用处是找土拨鼠的时间,存在dep里
void dfs1(int x,int depth,int fx)
{
	blk[x]=depth/2;
	for(int i=0;i<vec[x].size();i++){
		int s=vec[x][i];
		if(s==fx) continue;
		dfs1(s,depth+1,x);
	}
}//找Orange的时间,存在blk里
void getans(int x,int fx)
{
	bool fl=0;
	for(int i=0;i<vec[x].size();i++){
		int s=vec[x][i];			
		if(s==fx) continue;fl=1;
		if(dep[s]>=blk[s]){//不合法的点
			ans=max(blk[x],ans);
			continue;
		}
		getans(s,x);
	}
	if(!fl){
		ans=max(ans,blk[x]);
	}//叶节点
}//然后以当前位置为根,找到每条链能走的最远的地方
int main()
{
	int x,y;
	scanf("%d%d",&n,&t);
	for(int i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		vec[x].push_back(y);
		vec[y].push_back(x);
	}
	dfs(1,0,-1);
	int gol=dep[n]-t,rt;//gol 走完ts后与n的距离 rt 根
	if(gol<=0){puts("0");return 0;}//如果ts后已经到了,那么Orange就直接抓住了土拨鼠
	for(int i=n,j=gol+1;j;i=fa[i],j--) rt=i;//找出t秒之后当前的位置,rt
	memset(dep,0,sizeof(dep));
	memset(blk,0,sizeof(blk));
	dfs(rt,0,-1);//得出土拨鼠的时间
	dfs1(n,1,-1);//得出Orange的时间
	ans=-1;getans(rt,-1);//得出答案
	printf("%d\n",ans);
}

END

这题可以只用一个dfs,然后思路差不多,只不过把时间换成是这个节点到两人的距离,用 l c a lca lca就可以过。其实没有什么本质的差别,但是我的做法还是被 D N d a l a o DNdalao DNdalao嫌弃了wuwuwu……

你可能感兴趣的:(2020牛客多校,树形dp)