P5021赛道修建题解

题目戳我

看到最小值最大,很容易想到二分。
那么我们想想怎么\(check\)

在找路径的时候,显然让路径越接近要\(check\)\(mid\)就越好。而且我们不能漫无目的的凑,要找个神奇的顺序,所以我们不妨使用\(dfs\)

\(dfs\)时,对于每一个节点,考虑它所有连着儿子的边。如果这条边的长度大于\(mid\),那么它本身就可以成为一条路径。反之,则找到另一条边\(j\),使得\(dis[i]+dis[j]>=mid\)且不存在边\(k\)使得\(dis[i]+dis[k]\geqslant mid\)\(\&\&\)\(dis[k]<dis[j]\)

如果还有剩下的边,那就考虑把这些边中最大的边接到当前节点上。

这些操作可以用\(multiset\)搞定(当然我写丑了要吸氧)

有关\(multiset\)

\(multiset\)就是可以有重复元素的\(set\)
常用函数:

multiset<int> q;
将x插入进multiset: q.insert(x)

删除所有值为x的元素: q.erase(x)

删除x这个位置的元素(此时x为迭代器): q.erase(x)

删除[l,r)这一段的元素: q.erase(l,r)

第一个元素的位置: q.begin()

最后一个元素的后一个位置: q.end()

最后一个元素的位置: q.rbegin()

第一个元素的前一个位置: q.rend()

寻找值为x的元素的位置: q.find(x)(找不到返回q.end())

找到第一个大于等于x的元素的位置: q.lower_bound(x)(失败返回q.end())

找第一个大于x的元素的位置: q.upper_bound(x)(失败返回q.end())

判空: q.empty()

清空: q.clear()

\(Code\)(要开\(c++11\)以及吸氧)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<deque>
#include<set>
#define pa pair<int,int>
#define rg register
#define ls (k<<1)
#define rs (k<<1|1)
using namespace std;
typedef long long ll;
const double eps=1e-13;
inline int read(){
	char ch=getchar();
    int x=0;bool f=0;
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return f?-x:x;
}
const int N=50009;
int n,m,head[N],cnt,l=2147483647,r,rtn;
struct E{
	int to,nxt,dis;
}ed[N<<1];
inline void add(int fr,int to,int dis){
	ed[++cnt].to=to;
	ed[cnt].dis=dis;
	ed[cnt].nxt=head[fr];
	head[fr]=cnt;
}	
int dfs(int u,int f,int x){
        multiset<int> q;
	for(rg int e=head[u];e;e=ed[e].nxt){
		int v=ed[e].to;
		if(v==f) continue;
		int d=ed[e].dis+dfs(v,u,x);
		if(d>=x) rtn++;//rtn统计找到的路径条数
		else q.insert(d);
	}
	if(q.empty()) return 0;
	int rr=0;
	while(!q.empty()){
		auto i=q.begin();
		int d=x-*i,qwq=*i;
		q.erase(i);//注意先删除当前元素,这样才能准确知道是否有和这个元素相等的符合条件的元素
		auto j=q.lower_bound(d);
		if(j==q.end()) rr=max(rr,qwq);//选择最长的接上去
		else{
			rtn++;
			q.erase(j);
		}
	}
	return rr;//返回接上去的长度
}
inline bool ck(int x){
	  rtn=0;
	  int orz=dfs(1,0,x);
	  if(rtn>=m) return 1;
	  return 0;
}
int main(){
	n=read();m=read();
	for(rg int i=1;i<n;i++){
		int fr=read(),to=read(),dis=read();
		add(fr,to,dis);add(to,fr,dis);
		r+=dis;l=min(l,dis);
	}
	r/=m;//讨论区大佬神奇的优化
	while(l<=r){
		int mid=(l+r)>>1;
		if(ck(mid)) l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",r);
}
posted @ 2020-09-05 10:01  千载煜  阅读(160)  评论(0编辑  收藏  举报