luogu P5021 賽道修建

背景:

一年前自己還是太菜了。
雖然現在也菜。

題目傳送門:

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

題意:

一棵樹,選擇mm條邊鋪設,要求這些邊不能有交集,且不允許掉頭(我們認爲它是從一點修到另外一點的),現在求這些賽道的最短值的最大值。

思路:

現在看來還是一個眼題的。
二分答案midmid
考慮兒子的信息通過當前的邊來合併。
若當前已經可以更新到midmid,直接方案數加11即可。
若當前不能更新到midmid,考慮從小到大選出儘可能小的能與它配對達到midmid的,將這兩個配對在一起(相當與這兩條路徑通過當前點連在一起),方案數加11
最後傳上去最大的即可。
可用multiset\text{multiset}維護即可。
注意multiset\text{multiset}eraseerase刪除值是將全部爲這個值的刪去。

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define ID multiset<int>::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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章