分析
我们有一个性质,树的任意一个点到树另外一个点的最长距离,一定是由某一个直径的端点和该点构成的。有了这个性质。我们只需要对这几个特殊的建一个虚树。然后找到直径的端点,那么答案为 这个做三次
就好了。代码非常简单,因为我们不用真正的建出虚树。
关于定理的证明,下文全选自
网站。
引理:在一个连通无向无环图中,
、
和
是三个不同的结点。当
到
的最短路与
到
的最短路不重合时,
到
的最短路就是这两条最短路的拼接。
证明:假设
到
有一条不经过
的更短路
,则该路与
、
形成一个环,与前提矛盾。
定理:在一个连通无向无环图中,以任意结点出发所能到达的最远结点,一定是该图直径的端点之一。
证明:假设这条直径是
。分两种情况:
当出发结点
在
时,假设到达的最远结点
不是
中的任一个。这时将
与不与之重合的
拼接(也可以假设不与之重合的是直径的另一个方向),可以得到一条更长的直径,与前提矛盾。
当出发结点
不在
上时,分两种情况:
当到达的最远结点
横穿
时,记与之相交的结点为
。此时有
。而此时
,故可得
。由 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); }