【校內模擬】深邃(貪心)(二分答案)

簡要題意:

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