P8314 [COCI 2021/2022 #4] Parkovi 题解

最大值最小,容易想到二分。

考虑贪心,尽量选深度小的点,判断最少点数是否 \(\le k\)。对每个点记录子树内距离最近的公园和最远的未被覆盖的点。若两距离和 \(>mid\),则选择当前点,时间复杂度 \(\mathcal O(n\log V)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 200003
#define pb push_back
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,k;
int tot,hd[mxn],vr[mxn<<1],ed[mxn<<1],nx[mxn<<1];
ll l,r,mid,d[mxn],f[mxn],d1[mxn],d2[mxn];
bool v[mxn];
inline void add(int x,int y,int z){
	vr[++tot]=y,ed[tot]=z,nx[tot]=hd[x],hd[x]=tot;
}
void dfs(int x,int fa,ll ds){
	f[x]=0;
	ll mx=-1e18,mn=1e18;
	for(int i=hd[x],y;i;i=nx[i])if((y=vr[i])!=fa){
		dfs(y,x,ed[i]);
		f[x]+=f[y];
		mn=min(mn,d1[y]+ed[i]);
	}
	if(mn>mid)mx=0;
	for(int i=hd[x],y;i;i=nx[i])if((y=vr[i])!=fa){
		if(mn+d2[y]+ed[i]>mid)mx=max(mx,d2[y]+ed[i]);
	}
	if(mx+ds>mid){
		f[x]++;
		d1[x]=0,d2[x]=-1e18;
		v[x]=1;
	}else d1[x]=mn,d2[x]=mx;
}
signed main(){
	scanf("%d%d",&n,&k);
	for(int i=1,x,y,z;i<n;++i){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	r=2e14;
	while(l<r){
		mid=(l+r)>>1;
		dfs(1,0,1e18);
		if(f[1]<=k)r=mid;
		else l=mid+1;
	}
	cout<<l<<'\n';
	rep(i,1,n)v[i]=0;
	mid=l,dfs(1,0,1e18);
	rep(i,1,n)if(v[i])cout<<i<<" ",k--;
	rep(i,1,n)if(!v[i]&&k)cout<<i<<" ",k--;
	return 0;
}
posted @ 2025-07-05 17:39  zifanwang  阅读(12)  评论(0)    收藏  举报