1. 程式人生 > >BZOJ 2654: tree(二分 最小生成樹)

BZOJ 2654: tree(二分 最小生成樹)

memory med har stat 連通 註意 題目 sub mit

Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 2901 Solved: 1196
[Submit][Status][Discuss]

Description

給你一個無向帶權連通圖,每條邊是黑色或白色。讓你求一棵最小權的恰好有need條白色邊的生成樹。 題目保證有解。

Input

第一行V,E,need分別表示點數,邊數和需要的白色邊數。 接下來E行,每行s,t,c,col表示這邊的端點(點從0開始標號),邊權,顏色(0白色1黑色)。

Output

一行表示所求生成樹的邊權和。 V<=50000,E<=100000,所有數據邊權為[1,100]中的正整數。

Sample Input

2 2 1
0 1 1 1
0 1 2 0

Sample Output

2

HINT

原數據出錯,現已更新 by liutian,但未重測---2016.6.24

Source

對於圖上的最小生成樹

如果我們得到的最小生成樹上的白邊小於$need$條,那麽說明白邊的權值整體偏大,

那麽我們考慮對所有的白邊減去一個權值,這樣最小生成樹上的白邊就會變多

這個過程很顯然具有單調性,於是可以二分減去的權值

註意一個坑:當權值相同的時候優先選擇白邊

#include<cstdio>
#include
<algorithm> using namespace std; const int MAXN = 1e6 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < 0 || c > 9) {if(c == -) f = -1; c = getchar();} while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); return x * f; }
int N, M, need; struct Edge { int u, v, w, opt; bool operator <(const Edge &rhs) const { return w == rhs.w ? opt < rhs.opt : w < rhs.w; } }E[MAXN], e[MAXN]; int Val = 0, fa[MAXN]; int siz[MAXN]; int find(int x) { return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); } int unionn(int x, int y) { int fx = find(x), fy = find(y); if(siz[fx] < siz[fy]) swap(fx, fy); fa[fy] = fx; } bool check(int x) { Val = 0; for(int i = 1; i <= M; i++) { E[i] = e[i]; if(e[i].opt == 0) E[i].w += x; } for(int i = 1; i <= N; i++) fa[i] = i, siz[i] = 1; int tot = 0, white = 0; sort(E + 1, E + M + 1); for(int i = 1; i <= M; i++) { if(find(E[i].u) != find(E[i].v)) { unionn(E[i].u, E[i].v); Val += E[i].w; tot++; if(E[i].opt == 0) white++; } if(tot == N - 1) break; } return white >= need; } main() { #ifdef WIN32 freopen("a.in", "r", stdin); #endif N = read(), M = read(), need = read(); for(int i = 1; i <= M; i++) { int x = read() + 1, y = read() + 1, z = read(), opt = read(); e[i] = (Edge){x, y, z, opt}; } int l = -110, r = 110, ans = 0; while(l <= r) { int mid = l + r >> 1; if(check(mid)) ans = Val - mid * need, l = mid + 1; else r = mid - 1; } printf("%d", ans); }

BZOJ 2654: tree(二分 最小生成樹)