简要题意:
一棵树,有 k k k 个关键点,请你把树划分为若干联通块,使得每个联通块包含至少一个关键点,最小化最大的联通块的大小。
题解:
首先容易注意到可以二分答案。
然后考虑怎么判断,进行dfs,每个点尽量贪心往下划分。
h v u hv_u hvu 表示 u u u 为根的子树, u u u 这个联通块还能多容呐的点数。
n t u nt_u ntu 表示 u u u 为根的子树,无法向下分到任何一个联通块的点数。
这样就非常好计算了,具体转移见代码。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}
}using namespace IO;
using std::cerr;
using std::cout;
cs int N=2e5+7;
int el[N],nx[N+N],to[N+N],ec;
void adde(int u,int v){
nx[++ec]=el[u],el[u]=ec,to[ec]=v;
nx[++ec]=el[v],el[v]=ec,to[ec]=u;
}
bool key[N];
int lim;
int hv[N],nt[N];
bool dfs(int u,int p){
hv[u]=nt[u]=0;
for(int re e=el[u];e;e=nx[e])
if(p!=to[e]&&!dfs(to[e],u))return false;
if(key[u])hv[u]=lim-1;else nt[u]++;
if(nt[u]>=lim)return false;
if(hv[u]<nt[u]){
if(u==1)return false;
nt[p]+=nt[u];
}else
hv[p]=std::max(hv[p],hv[u]-nt[u]);
return true;
}
void Main(){
int n=gi(),k=gi();
for(int re i=1;i<n;++i)
adde(gi(),gi());
for(int re i=1;i<=k;++i)
key[gi()]=true;
int l=1,r=n,ans=-1;
while(l<=r){
lim=(l+r)>>1;
if(dfs(1,0))ans=lim,r=lim-1;
else l=lim+1;
}cout<<ans<<"\n";
}
inline void file(){
#ifdef zxyoi
freopen("deep.in","r",stdin);
#else
freopen("deep.in","r",stdin);
freopen("deep.out","w",stdout);
#endif
}signed main(){file();Main();return 0;}