题目描述

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

输入格式

第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

输出格式

一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

样例

样例输入

2 2 1
0 1 1 1
0 1 2 0

样例输出

2

数据范围与提示

原数据出错,现已更新 by liutian,但未重测---2016.6.24

题解:

我们要求一个最小生成树,树中必须恰好包含need条边

在常用的kruskal算法中,一旦边权确定,黑白边就是等价的,但我们要找恰好need条边,所以要让白边尽可能的“特殊”。

想让白边“特殊”,只能给白边改权值。

设用当前的权值求出的最小生成树所含有的白边>need,我们要减少白边数量,就要给每个白边加权值;小于need,要增加白边数量,给白边减权值(加上一个负权)。

那真正的权值怎么求?

我们在kruskal中记录白边个数,白边个数>=need时更新权值:ans=ans-mid×need

一定是白边个数>=need时更新权值,博主在这里卡了半天。

具体原因博主也是看的大佬的题解,现给出解释:

如果在你的二分过程中如果给白边加上mid,你得到的白边数比need大。

给白边加上mid+1,你得到的白边比need小。

这种情况看似没法处理。

但是考虑一下kruskal的加边顺序

可以发现如果出现这种情况,一定是有很多相等的白边和黑边。

因为你排序的时候如果有两条相同权值的黑边和白边,肯定是要把白边排在前面

因为数据保证合法,所以我们可以把一些白边替换成黑边。

所以我们要在白边数>=need的时候更新答案。

那么这个增加的权值如何确定?

二分答案

二分枚举要加上的权值,然后给每个白边加上权值,跑kruskal,出的最小生成树所含有的白边>need,l=mid+1,小于need,r=mid-1。

跑完每遍kruskal后把白边的权值再减回去。

二分结束后,输出当前的实际权值。

#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXE 100005
#define MAXV 50005
using namespace std;
int v, e, need;
struct node {
int fr, to, w, col;
friend bool operator < (const node &a, const node &b) {
return (a.w == b.w) ? (a.col < b.col) : (a.w < b.w);
}
} edge[MAXE];
int l = -101, r = 101, mid, ans = 0, res = 0;
int fa[MAXV];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
int kruskal(int mid) {
int sum_edge = 0, sum_white = 0;
res = 0;
for (int i = 1; i <= v; i++) fa[i] = i;
for (int i = 1; i <= e; i++) {
if (!edge[i].col)
edge[i].w += mid;
}
sort(edge + 1, edge + e + 1);
for (int i = 1; i <= e; i++) {
int x = find(edge[i].fr), y = find(edge[i].to);
if (x != y) {
fa[x] = y;
res += edge[i].w;
sum_edge++;
if (!edge[i].col)
sum_white++;
}
if (sum_edge == v - 1)
break;
}
for (int i = 1; i <= e; i++) {
if (!edge[i].col)
edge[i].w -= mid;
}
return sum_white;
}
int main() {
scanf("%d%d%d", &v, &e, &need);
for (int i = 1; i <= e; i++) {
scanf("%d%d%d%d", &edge[i].fr, &edge[i].to, &edge[i].w, &edge[i].col);
edge[i].fr++, edge[i].to++;
}
while (l <= r) {
mid = (l + r) >> 1;
if (kruskal(mid) >= need) {
l = mid + 1;
ans = res - mid * need;
} else
r = mid - 1;
}
printf("%d\n", ans);
return 0;
}

最新文章

  1. PHP定界符使用技巧
  2. Python之路,day8-Python基础
  3. 网络存储技术(3) based on zt
  4. c# json转换实例
  5. POJ 1064 Cable master (二分)
  6. 内部类 &amp; 泛型
  7. Java三大特征之------多态
  8. 【Android】Handler使用入门
  9. 教程-Delphi各版本与工具下载地址
  10. pyqt listview基础学习01
  11. android onKeydown
  12. NOI 评价体系 arbiter 安装方法 常见的问题 移植
  13. Exameple014实现html中checkbox的全选,反选和全不选(1)
  14. JavaWeb程序利用Servlet的对SQLserver增删改查操作
  15. Leetcode题解(十八)
  16. Java基础(五)-Java序列化与反序列化
  17. CCF系列之ISBN号码(201312-2)
  18. linux下安装python3
  19. VulDeePecker:基于深度学习的脆弱性检测系统
  20. 历经15个小时,终于评出这8本最受欢迎的SQL书籍

热门文章

  1. Scala 方法与函数简单记录
  2. thinkphp 分布式数据库支持
  3. csp-s模拟9697题解
  4. 计算几何——线段和直线判交点poj3304
  5. Java中的栈,堆,方法区和常量池
  6. day 80 Vue学习一之vue初识
  7. &lt;数据可视化&gt;样例+数据+画图
  8. java_IO流(输入流)
  9. JS数组 了解成员数量(数组属性length) myarr.length
  10. System.UriFormatException: Invalid URI 解决方法