NOIP2018 D1T3赛道修建

题目链接:Click here

Solution:

最小值最大,考虑二分一个答案(k)

考虑在子树内先匹配,最后传递一个值给自己的父亲(因为每条边只能用一次,所以一颗子树最多传递一个值)

那么我们就可以用一个(multiset)来存子树的传递值,然后匹配,将剩下的边传递上去就行了

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
int n,m,cnt,num,l=1,r,u,head[N];
struct Edge{int nxt,to,val;}edge[N<<1];
void ins(int x,int y,int z){
	edge[++cnt].nxt=head[x];
	edge[cnt].to=y;edge[cnt].val=z;
	head[x]=cnt;
}
void dfs(int x,int fa,int dis){
	if(dis>r) r=dis,u=x;
	for(int i=head[x];i;i=edge[i].nxt)
		if(edge[i].to!=fa) dfs(edge[i].to,x,dis+edge[i].val);
}
multiset<int> q[N];
multiset<int>::iterator it;
int calc(int x,int fa,int k){
	q[x].clear();
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to,v=0;
		if(y==fa) continue;
		v=calc(y,x,k)+edge[i].val;
		if(v>=k) ++num;else q[x].insert(v);
	}int maxv=0;
	while(!q[x].empty()){
        if(q[x].size()==1) return max(maxv,*q[x].begin());
        int v=*q[x].begin();
		it=q[x].lower_bound(k-v);
        if(it==q[x].begin()&&q[x].count(*it)==1) ++it;
        if(it==q[x].end()){
            maxv=max(maxv,v);
            q[x].erase(q[x].find(*q[x].begin()));
        }else{++num;
            q[x].erase(q[x].find(*it));
            q[x].erase(q[x].find(v));
        }
    }return maxv;
}
int check(int k){
	num=0;calc(1,0,k);
	return num>=m;
}
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read(),z=read();
		ins(x,y,z),ins(y,x,z);
	}dfs(1,0,0);dfs(u,0,0);
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid)) l=mid+1;
		else r=mid-1;
	}printf("%d
",l-1);
	return 0;
}
原文地址:https://www.cnblogs.com/NLDQY/p/11433170.html