分析

我们有一个性质,树的任意一个点到树另外一个点的最长距离,一定是由某一个直径的端点和该点构成的。有了这个性质。我们只需要对这几个特殊的建一个虚树。然后找到直径的端点,那么答案为 这个做三次 就好了。代码非常简单,因为我们不用真正的建出虚树。

  • 关于定理的证明,下文全选自 网站。

  • 引理:在一个连通无向无环图中, 是三个不同的结点。当 的最短路与 的最短路不重合时, 的最短路就是这两条最短路的拼接。

  • 证明:假设 有一条不经过 的更短路 ,则该路与 形成一个环,与前提矛盾。

  • 定理:在一个连通无向无环图中,以任意结点出发所能到达的最远结点,一定是该图直径的端点之一。

  • 证明:假设这条直径是 。分两种情况:

  • 当出发结点 时,假设到达的最远结点 不是 中的任一个。这时将 与不与之重合的 拼接(也可以假设不与之重合的是直径的另一个方向),可以得到一条更长的直径,与前提矛盾。

  • 当出发结点 不在 上时,分两种情况:
    到达的最远结点 横穿 时,记与之相交的结点为 。此时有 。而此时 ,故可得 。由 1 的结论可知该假设不成立。

  • 到达的最远结点 不相交时,记 的最短路首先与 相交的结点是 。由假设 。而 又可以形成 ,而 ,与题意矛盾。

  • 因此定理成立。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 110000;
    vector<int> G[N];int read() {int x;scanf("%d",&x);return x;}
    int n,vis[N],dis1[N],dis2[N],dis3[N],L,R,m,k;
    void dfs1(int x,int fa,int dep) {dis1[x]=dep;for(auto y:G[x])if(y^fa)dfs1(y,x,dep+1);}
    void dfs2(int x,int fa,int dep) {dis2[x]=dep;for(auto y:G[x])if(y^fa)dfs2(y,x,dep+1);}
    void dfs3(int x,int fa,int dep) {dis3[x]=dep;for(auto y:G[x])if(y^fa)dfs3(y,x,dep+1);}
    int main() {
      n = read();m = read();k = read();int ans = 0;
      for(int i = 1,a;i <= m;i++) {a = read();vis[a] = 1;}
      for(int i = 1,a,b;i <= n - 1;i++) {a = read();b = read();G[a].push_back(b);G[b].push_back(a);}
      dfs1(1,0,0);for(int i = 1;i <= n;i++) if(dis1[i] >= dis1[L] && vis[i]) L = i;
      dfs2(L,0,0);for(int i = 1;i <= n;i++) if(dis2[i] >= dis2[R] && vis[i]) R = i;
      dfs3(R,0,0);for(int i = 1;i <= n;i++) if((dis3[i] <= k && dis2[i] <= k)) ans++;
      return 0 * printf("%d\n",ans);
    }