洛谷P5021 赛道修建

话说去年为什么暴力炸成了15......

其实我现在都不会做,参考的一位P党大佬的题解,写成了C++版而已(附注了一些关键部分的细节)。

总之现在弄懂了QWQ

代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e6+10,maxm=2e6+10,inf=1e8;
int bg[maxn],nt[maxm],to[maxm],w[maxm],e;
int n,m,ed[maxn],num,st[maxn],top,cnt,up[maxn],ans;

void insert(int x,int y,int z) {
	nt[++e]=bg[x];
	to[e]=y;
	w[e]=z;
	bg[x]=e;
}

void dfs(int x,int fa,int lim) {
	int i,j,u;
	up[x]=0;
	for (i=bg[x];i;i=nt[i]) {
		u=to[i];
		if (u==fa) continue;
		dfs(u,x,lim);
	}
	num=0;
	for (i=bg[x];i;i=nt[i]) {
		u=to[i];
		if (u==fa) continue; //PS:一顿苦调代码,结果好久才发现是这句话忘写了QAQ
		ed[++num]=w[i]+up[u]; //每条边继承子节点的上传值
	}
	//贪心:
	sort(ed+1,ed+num+1); //排完序使得边满足单调性
	i=1; j=num; top=0; 
	while (ed[j]>=lim) { //假如大边可以自己(连带其子节点的上传值,之后省略这句话)成为一条赛道
		j--;
		cnt++;
	}
	while (i<=j) {
		while (ed[i]+ed[j]>=lim && i<j) {
			st[++top]=ed[j]; j--; //如果此时的较大边加上较小边满足条件,就将大边指针向左移,st内存的是囤积的大边(单调栈,top越大边权越小);
		}
		if (top) {top--; cnt++;} //如果有大边的存货就拿出一条与当前的小边匹配,因为此时小边一定大于之前的小边,所以一定能匹配上之前囤的大边中的最小值;
		else up[x]=ed[i]; //没有存货说明此时的小边没有匹配,因此将其上传(若up之前有东西也一定只是一条小于此时边的小边)
		i++;
	}
	cnt+=top/2; //如果还存在大边剩余,就两两配对
	if (top%2) up[x]=st[1]; //配对完还剩一个,就将最大的大边上传(之后的大边两两配对去,反正可以配好)
}

int ok(int x) {
	int i;
	cnt=0;
	dfs(1,0,x);
	return cnt>=m;
}

int main() {
	int i,x,y,z,l=inf,r=0,mid;
	scanf("%d%d",&n,&m);
	for (i=1;i<n;i++) {
		scanf("%d%d%d",&x,&y,&z);
		insert(x,y,z);
		insert(y,x,z);
		r+=z;
		l=min(l,z);
	}
	r/=m;
	while (l<=r) {
		mid=(l+r)/2;
		if (ok(mid)) {
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	printf("%d\n",ans);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值