[BZOJ 2654] tree
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=2654
[算法]
给白色边都加上一个值,会使得最小生成树上的白边数量减少,不妨二分给白色边加上的值 , 检验答案时用Kruskal求最小生成树即可
时间复杂度 : O(NlogN)
[代码]
#include<bits/stdc++.h> using namespace std; #define MAXN 100010 #define MAXM 200010 struct edge { int u , v , w , type; } e[MAXN]; int tot , n , m , value , ans; int fa[MAXN]; template <typename T> inline void chkmax(T &x , T y) { x = max(x , y); } template <typename T> inline void chkmin(T &x , T y) { x = min(x , y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline int get_root(int x) { if (fa[x] == x) return x; return fa[x] = get_root(fa[x]); } inline bool cmp(edge a , edge b) { return a.w == b.w ? a.type < b.type : a.w < b.w; } inline bool check(int mid) { int cnt = 0; tot = 0; for (int i = 1; i <= n; i++) fa[i] = i; for (int i = 1; i <= m; i++) if (!e[i].type) e[i].w += mid; sort(e + 1 , e + m + 1 , cmp); for (int i = 1; i <= m; i++) { int su = get_root(e[i].u) , sv = get_root(e[i].v); if (su != sv) { fa[su] = sv; tot += e[i].w; if (!e[i].type) ++cnt; } } for (int i = 1; i <= m; i++) if (!e[i].type) e[i].w -= mid; return cnt >= value; } int main() { read(n); read(m); read(value); for (int i = 1; i <= m; i++) { read(e[i].u); read(e[i].v); read(e[i].w); ++e[i].u; ++e[i].v; read(e[i].type); } int l = -105 , r = 105 , ans = 0; while (l <= r) { int mid = (l + r) / 2; if (check(mid)) { ans = tot - value * mid; l = mid + 1; } else r = mid - 1; } printf("%d\n" , ans); return 0; }