题目大意
- 给定一棵有n个节点,n-1条边的树,每条边长度为1,开始土拨鼠在1号节点,沿着最短路走向n号。
- 土拨鼠出发t秒钟后,Orange以2m/s的速度跑向土拨鼠,而土拨鼠以1m/s的速度向任意方向逃跑。设每秒钟土拨鼠先移动Orange再移动。土拨鼠可以选择留在原地。
- 求Orange最晚几秒钟能追上他。
解题思路
思路一
二分时间t,判断在t内土拨鼠是否会被追上。
每次都以Orange所在的寝室为根建树,从1到n枚举所有的土拨鼠能够到达的点,然后判断在走的过程中会不会被追上。
这个思路比赛时想到过,就是走一步看一步,只是后来举出几个“反例”,以为很难实现。
思路二
- 我们先以Orange为起点,DFS出Orange到每个点的时间,记为O数组;
再以走了t秒后土拨鼠的位置为起点DFS,求出它到每个点的时间,记为G数组。
这里的所有时间都不包含弯路,因为土拨鼠走弯路就相当于缩短距离白给。 - 最后再次DFS,寻找最远的第一个O值与G值重合的点,即土拨鼠最远能跑到的位置。
- 但是,也有无处可逃,原地等的情况,而有时这样会成为最佳方案。在判断时就取Orange来收人的O数组即可。
时间复杂度
思路一为O(n log n),思路二为O(n)(实际上是三次DFS)。
AC代码(思路二)
思路一的代码等标程出了再打吧(咕咕咕)
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
vector<int> v[N];
int G[N],O[N],f[N],n,ans=-1;
void dfs(int x,int step,int fa)
{
O[x]=step/2;
int t,i;
for(i=0;i<v[x].size();i++)
{
t=v[x][i];
if(t==fa) continue;
dfs(t,step+1,x);
}
}
void ddfs(int x,int step,int fa)
{
G[x]=step;
for(int i=0;i<v[x].size();i++)
{
int t=v[x][i];
if(t==fa) continue;
f[t]=x;
ddfs(t,step+1,x);
}
}
void dddfs(int x,int fa)
{
bool flag=0;
int t,i;
for(i=0;i<v[x].size();i++)
{
t=v[x][i];
if(t==fa) continue;
flag=1;
if(G[t]>=O[t])
{
ans=max(ans,O[x]);
continue;
}
dddfs(t,x);
}
if(!flag) ans=max(ans,O[x]);
}
int main()
{
int t,x,y,i,j;
scanf("%d%d",&n,&t);
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
ddfs(1,0,-1);
x=G[n]-t;
if(x<=0) puts("0");
else
{
i=n,j=x+1;
while(j) y=i,i=f[i],j--;
dfs(n,1,-1);
ddfs(y,0,-1);
dddfs(y,-1);
printf("%d\n",ans);
}
}
京公网安备 11010502036488号