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