题解
1.假设有一个以标记点 \(c\) 为根的子树,且子树内没有其他标记点,易得该子树内所有点的 \(f\leq f(c)\),所以我们可以把该子树内的非标记点全部删掉
2.完成步骤1之后,图就变成了所有叶子节点均为标记点的树
3.题目等价于求该树内,最小的点到边界的最大值,也就是求树的直径的一半
4.为什么?我们把树的直径高亮,对于任意点有 \(f\geq k+len/2\) (先到中点,再到两端)
实施
以标记点为根建树,直径一定经过某个点,所以树形 \(dp\),经过某个点的最长直径等于 第一大子树高度 + 第二大子树高度
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
vector<int> G[200005];
int mark[200005];
int dis[200005];
int vis[200005];
int len=0;
void dfs(int now,int fa)
{
int max1=0,max2=0;
if(mark[now]) vis[now]=1;
for(auto next:G[now])
{
if(next==fa) continue;
dfs(next,now);
if(vis[next])
{
if(dis[next]+1>=max1)
{
max2=max1;
max1=dis[next]+1;
}
else if(dis[next]+1>max2) max2=dis[next]+1;
vis[now]=1;
}
}
dis[now]=max1;
len=max(len,max1+max2);
}
void solve()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
G[i].clear();
vis[i]=0;
mark[i]=0;
dis[i]=0;
}
int start=0;
for(int i=1;i<=k;i++)
{
int x;
cin>>x;
start=x;
mark[x]=1;
}
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
dfs(start,start);
cout<<(len-len/2)<<'\n';
len=0;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--) solve();
return 0;
}
标签:Distance,int,max1,Maximum,next,max2,Minimum,now,dis
From: https://www.cnblogs.com/pure4knowledge/p/18301696