[Luogu P2619] [BZOJ 2654] [国家集训队2]Tree I

探讨了在无向带权连通图中,通过调整白色边权值并使用二分搜索与Kruskal算法,寻找包含特定数量白色边的最小生成树的优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
BZOJ传送门

题目描述

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 n e e d need need条白色边的生成树。

题目保证有解。

输入输出格式

输入格式:

第一行 V , E , n e e d V,E,need V,E,need分别表示点数,边数和需要的白色边数。

接下来 E E E

每行 s , t , c , c o l s,t,c,col s,t,c,col表示这边的端点(点从 0 0 0开始标号),边权,颜色( 0 0 0白色 1 1 1黑色)。

输出格式:

一行表示所求生成树的边权和。

输入输出样例

输入样例#1:
2 2 1
0 1 1 1
0 1 2 0
输出样例#1:
2

说明

0 : V ≤ 10 0:V\le 10 0:V10

1 , 2 , 3 : V ≤ 15 1,2,3:V\le 15 1,2,3:V15

0 , . . , 19 : V ≤ 50000 , E ≤ 100000 0,..,19:V\le 50000,E\le 100000 0,..,19:V50000,E100000

所有数据边权为 [ 1 , 100 ] [1,100] [1,100]中的正整数。

By WJMZBMR

解题分析

按权值排序先选出前 n e e d need need条白色边, 再直接上 k r u s k a l kruskal kruskal显然不能保证可以取到最优解。

我们想要取到恰好 n e e d need need条边, 又要最优, 还有一个方法:魔改白色边的权值。 我们二分一个值 d e l del del, 使得所有白色边加上这个权值之后最小生成树恰好有 n e e d need need条边即可。

但这样可能有个潜在的问题:我们可能二分不到这个恰好的点, 最后的答案多出了几条白色的边。 解决方法其实很简单:直接加上多出的条数$\times 二 分 到 的 权 值 即 可 。 因 为 当 这 个 权 值 二分到的权值即可。 因为当这个权值 +1$的时候黑色边更多, 而我们排序的时候是将同权值的白色边排在前面的, 所以肯定有足够数量的黑色边作为替代。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int head[MX], bel[MX];
int del, dot, line, cnt, k, tot, ct;
struct Edge {int from, to, val, col;}edge[MX << 1];
IN bool operator < (const Edge &x, const Edge &y)
{
	int key1 = x.val + x.col * del, key2 = y.val + y.col * del;
	return key1 == key2 ? x.col > y.col : key1 < key2;
}
int find (R int now) {return bel[now] == now ? now : bel[now] = find(bel[now]);}
IN bool check()
{
	int bela, belb; ct = tot = 0;
	std::sort(edge + 1, edge + 1 + line);
	for (R int i = 0; i <= dot; ++i) bel[i] = i;
	for (R int i = 1; i <= line; ++i)
	{
		bela = find(edge[i].from), belb = find(edge[i].to);
		if(bela == belb) continue;
		ct += edge[i].col, tot += edge[i].val;
		bel[bela] = belb;
	}
	return ct >= k;
}
int main(void)
{
	in(dot), in(line), in(k);
	for (R int i = 1; i <= line; ++i)
	{
		in(edge[i].from), in(edge[i].to);
		in(edge[i].val), in(edge[i].col); edge[i].col ^= 1;
	}
	int lef = -100, rig = 100, mid, ans;
	W (lef < rig)
	{
		mid = del = lef + rig + 1>> 1;
		if(check()) lef = mid;
		else rig = mid - 1;
	}
	del = lef; check();
	printf("%d\n", tot + (ct - k) * lef);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值