版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82989546
洛谷传送门
BZOJ传送门
题目描述
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 条白色边的生成树。
题目保证有解。
输入输出格式
输入格式:
第一行 分别表示点数,边数和需要的白色边数。
接下来 行
每行 表示这边的端点(点从 开始标号),边权,颜色( 白色 黑色)。
输出格式:
一行表示所求生成树的边权和。
输入输出样例
输入样例#1:
2 2 1
0 1 1 1
0 1 2 0
输出样例#1:
2
说明
所有数据边权为 中的正整数。
By WJMZBMR
解题分析
按权值排序先选出前 条白色边, 再直接上 显然不能保证可以取到最优解。
我们想要取到恰好 条边, 又要最优, 还有一个方法:魔改白色边的权值。 我们二分一个值 , 使得所有白色边加上这个权值之后最小生成树恰好有 条边即可。
但这样可能有个潜在的问题:我们可能二分不到这个恰好的点, 最后的答案多出了几条白色的边。 解决方法其实很简单:直接加上多出的条数$\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);
}