NOIp2018 D1T3 赛道修建

传送门
参考博客
如何检验答案?考虑贪心。令当前二分的答案为 k k k
我们考虑一个节点 u u u,如果从它子树走向它,可以凑出长度大等于 k k k的边,我们就给答案加上一。否则我们把这条链存起来。
遍历完它的儿子以后,我们存了一些长度不满 k k k的边,考虑用它们凑出长度大等于 k k k的边。凑得出来,就删掉这两条边,给答案加上一。如果剩的有边,我们就把最大的一条给传上去。如果不剩边,就把 0 0 0传上去。
实际上就是贪心地凑边。在感性认知上,这样好像是对的。。要先求直径确定二分上界,防止被菊花图卡掉。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+10;
int Head[maxn],Next[maxn<<1],V[maxn<<1],W[maxn<<1],cnt=0;
int n,m,u,v,w,len,ans=0;
multiset<int> s[maxn];
multiset<int>::iterator it;
inline void add(int u,int v,int w){Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,Head[u]=cnt;}
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x;
}

//求直径 
int dfs1(int u,int fa){
	int sum1=0,sum2=0;
	for(int i=Head[u];i;i=Next[i]) if(V[i]!=fa){
		sum2=max(sum2,dfs1(V[i],u)+W[i]);
		if(sum1<sum2) swap(sum1,sum2);
	}len=max(len,sum1+sum2);
	return sum1;
}
inline int dfs(int u,int fa,int k){
	s[u].clear();
	for(int i=Head[u];i;i=Next[i]) if(V[i]!=fa){
		int val=dfs(V[i],u,k)+W[i];
		if(val>=k) ++ans;
		else s[u].insert(val);
	}
	int mx=0;
	while(!s[u].empty()){
		if(s[u].size()==1) return max(mx,*s[u].begin());
		it=s[u].lower_bound(k-*s[u].begin());
		if(it==s[u].begin()&&s[u].count(*it)==1) it++;
		if(it==s[u].end()){
			mx=max(mx,*s[u].begin());
			s[u].erase(s[u].find(*s[u].begin()));
		}
		else{
			++ans;
			s[u].erase(s[u].find(*it));
			s[u].erase(s[u].find(*s[u].begin()));
		}
	}return mx;
}
inline bool check(int k){
	ans=0,dfs(1,0,k);
	return ans>=m;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<n;++i)
		u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
	dfs1(1,0);
	int l=1,r=len;
	while(l<r){
		int mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}printf("%d\n",l);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值