题解 P2619 【[国家集训队2]Tree I】
年轻人的第一道国家集训队题目
如果我们不做任何处理,直接跑MST(Minimum Spanning Tree,最小生成树),结果会有三种:
-
正好跑出
\text{Need} 条白边 -
白边多了
-
白边少了
第一种情况自然是最好的
剩下两种情况如何解决?
引起白边少的原因:黑边的边权相对较小,程序贪心地选择了更多的黑边
引起白边多的原因:白边的边权相对较小,程序贪心地选择了更多的白边
那么如果我们给白边相应地减去/加上一些边权,不就可以达成目标了?
考虑二分答案。
我们二分一个
边界分别是边权最小值(-100)和边权最大值(100)
由于题面保证有答案,所以直接输出
Check(mid)
怎么写?
我们将所有白边的边权加上
注意不要忘了把边权减回来
(不要在意
刚才我们不是记录了一下
答案不要忘了减去加上的边权(也就是
那么最后的答案就是
代码实现
#include <algorithm>
#include <iostream>
#include <cstdio>
#define FILE_IN(__fname) freopen(__fname, "r", stdin)
#define FILE_OUT(__fname) freopen(__fname, "w", stdout)
#define IMPROVE_IO() std::ios::sync_with_stdio(false)
#define WHITE 0
#define BLACK 1
using std::cin;
using std::cout;
using std::endl;
using std::max;
const int MAXV = 50000 + 10;
const int MAXE = 100000 + 10;
const int MAXW = 100;
struct Edge {
int prev, next, weight, add;
bool color;
// 1 -> black, 0 -> white
Edge() { prev = next = weight = color = add = 0; }
bool operator < (const Edge &that) const {
if (weight == that.weight) return color < that.color;
return weight < that.weight;
}
} edge[MAXE << 1];
int V, E, Need, cnt, ans;
int U[MAXV << 1];
int Find(int x) {
if (U[x] == x) return U[x];
return U[x] = Find(U[x]);
}
bool Union(int x, int y) {
x = Find(x), y = Find(y);
if (x == y) return false;
U[x] = y;
return true;
}
int Kruskal() {
int whiteEdge = 0;
for (int i = 1; i <= V; ++i) U[i] = i;
std::sort(edge + 1, edge + 1 + E);
int tot = 0;
ans = 0;
for (int i = 1; i <= E; ++i) {
if (Union(edge[i].prev, edge[i].next))
ans += edge[i].weight, ++tot, whiteEdge += (edge[i].color == WHITE);
if (tot == V - 1) break;
}
return whiteEdge;
}
bool Check(int add) {
for (int i = 1; i <= E; ++i) {
if (edge[i].color == WHITE) edge[i].weight += add;
}
bool Ans = (Kruskal() >= Need);
for (int i = 1; i <= E; ++i) {
if (edge[i].color == WHITE) edge[i].weight -= add;
}
return Ans;
}
int main() {
IMPROVE_IO();
cin >> V >> E >> Need;
for (int i = 1; i <= E; ++i) {
cin >> edge[i].prev >> edge[i].next >> edge[i].weight >> edge[i].color;
++edge[i].prev;
++edge[i].next;
}
int l = -MAXW, r = MAXW;
int Run = 0;
while (l <= r) {
int mid = ((l + r) >> 1);
if (Check(mid)) {
l = mid + 1;
Run = mid;
} else {
r = mid - 1;
}
}
Check(Run);
cout << ans - Need * Run << endl;
return 0;
}