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

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82989546

洛谷传送门

BZOJ传送门

题目描述

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

题目保证有解。

输入输出格式

输入格式:

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

接下来 E E

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

输出格式:

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

输入输出样例

输入样例#1:

2 2 1
0 1 1 1
0 1 2 0

输出样例#1:

2

说明

0 : V 10 0:V\le 10

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

0 , . . , 19 : V 50000 , E 100000 0,..,19:V\le 50000,E\le 100000

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

By WJMZBMR

解题分析

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

我们想要取到恰好 n e e d need 条边, 又要最优, 还有一个方法:魔改白色边的权值。 我们二分一个值 d e l del , 使得所有白色边加上这个权值之后最小生成树恰好有 n e e d 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);
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/82989546