[NOIP2018 Day1 T3]賽道修建 Solution

題意:給一棵樹,現在要從上面劃出mm條互不相交的鏈(指邊不能共用),問這些鏈中長度最短的最長可以達到多少。
最短最長沒什麼好說的了,考慮二分。
考慮如何判定?
使用貪心。
給每個點都開一個multisetmultiset,然後將子樹拼接後剩下的最長鏈插入這個點的multisetmultiset裏面,然後從左往右檢索該multisetmultiset,對於每個值二分出第一個值使得它們拼接後大於等於二分的值,然後拼接,最後把剩下的數裏面最大的往上傳即可。
code:code:

#include <bits/stdc++.h>
using std::cin;
using std::cout;
#define regi register int
int n,m;
int head[500001],tot;
std::multiset<int>chain[100001];
std::multiset<int>::iterator left,right;
int Make;
struct edge{
	int to;
	int nxt;
	int w;
}e[1000001];
inline void add(int x,int y,int z){
	e[++tot]={y,head[x],z};
	head[x]=tot;
}
int dfs(int x,int f,int Long){
	chain[x].clear();
	for(regi i=head[x];i;i=e[i].nxt){
		regi y=e[i].to;
		if(y==f)
		  continue;
		regi tmp=dfs(y,x,Long)+e[i].w;
		if(tmp>=Long){
			++Make;
			continue;
		}
	  chain[x].insert(tmp);
	}
	int max=0;
  for(left=chain[x].begin();left!=chain[x].end();++left){
    right=chain[x].lower_bound(std::max(Long-*left,*left+1));
    if(right!=chain[x].end()&&*left+*right>=Long){
    	chain[x].erase(right); 
    	++Make;
    }
    else
      if(left!=--chain[x].end()&&*left*2>=Long){
      	chain[x].erase(++left);
      	++Make;
      }
    else
      max=std::max(max,*left);
  }
  return max;
}
inline bool check(int x){
	Make=0;
  dfs(1,0,x);
  if(Make>=m)
    return 1;
  return 0;
}
main(){
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
  cin>>n>>m;
  for(regi i=1,x,y,z;i<n;++i){
  	cin>>x>>y>>z;
  	add(x,y,z);
  	add(y,x,z);
  }
  int l=1,r=0x3f3f3f3f;
  while(l<r){
  	regi mid=l+r+1>>1;
  	if(check(mid))
  	  l=mid;
  	else
  	  r=mid-1;
  }
  cout<<l;
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章