【校内模拟】深邃(贪心)(二分答案)

简要题意:

一棵树,有 kk 个关键点,请你把树划分为若干联通块,使得每个联通块包含至少一个关键点,最小化最大的联通块的大小。


题解:

首先容易注意到可以二分答案。

然后考虑怎么判断,进行dfs,每个点尽量贪心往下划分。

hvuhv_u 表示 uu 为根的子树,uu 这个联通块还能多容呐的点数。

ntunt_u 表示 uu 为根的子树,无法向下分到任何一个联通块的点数。

这样就非常好计算了,具体转移见代码。


代码:

#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;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章