题目大意

  • 给定一棵有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);
    }
}