luogu P5021 赛道修建

背景:

一年前自己还是太菜了。
虽然现在也菜。

题目传送门:

https://www.luogu.org/problem/P5021

题意:

一棵树,选择 m m m条边铺设,要求这些边不能有交集,且不允许掉头(我们认为它是从一点修到另外一点的),现在求这些赛道的最短值的最大值。

思路:

现在看来还是一个眼题的。
二分答案 m i d mid mid
考虑儿子的信息通过当前的边来合并。
若当前已经可以更新到 m i d mid mid,直接方案数加 1 1 1即可。
若当前不能更新到 m i d mid mid,考虑从小到大选出尽可能小的能与它配对达到 m i d mid mid的,将这两个配对在一起(相当与这两条路径通过当前点连在一起),方案数加 1 1 1
最后传上去最大的即可。
可用 multiset \text{multiset} multiset维护即可。
注意 multiset \text{multiset} multiset e r a s e erase erase删除值是将全部为这个值的删去。

代码:

#include
#include
#include
#include
#define ID multiset::iterator
using namespace std;
multiset<int> F[50010];
	int n,m,len=0,tot,limit,ans;
	struct node{int x,y,z,next;} a[100010];
	int last[50010];
void ins(int x,int y,int z)
{
	a[++len]=(node){x,y,z,last[x]}; last[x]=len;
}
void dfs(int x,int fa)
{
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y==fa) continue;
		dfs(y,x);
		if(F[y].size())
		{
			int op=*(--F[y].end());
			if(op+a[i].z>=limit) tot++; else F[x].insert(op+a[i].z);
		}
		else
		{
			if(a[i].z>=limit) tot++; else F[x].insert(a[i].z);
		}
	}
	int ma=-1;
	while(F[x].size())
	{
		int op=*F[x].begin();
		F[x].erase(F[x].begin());
		ID pos=F[x].lower_bound(limit-op);
		if(pos!=F[x].end()) F[x].erase(pos),tot++; else ma=op;
	}
	if(ma!=-1) F[x].insert(ma);
}
bool check(int x)
{
	for(int i=1;i<=n;i++)
		F[i].clear();
	tot=0;
	limit=x;
	dfs(1,0);
	return tot>=m;
}
int main()
{
	int x,y,z,l=0,r=0,mid;
	scanf("%d %d",&n,&m);
	for(int i=1;i<n;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		ins(x,y,z),ins(y,x,z);
		r+=z;
	}
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid)) l=mid+1,ans=mid; else r=mid-1;
	}
	printf("%d",ans);
}

你可能感兴趣的:(#,二分,#,STL,二分,贪心)