BZOJ 2654 tree 2分+最小生成树

BZOJ 2654 tree 二分+最小生成树

题目大意

给出一些边,每个边有一个边权和颜色。现在要求出最小边权有need个白边的生成树。输出这个边权。

思路

在白边上加一个权值,这样就可以人为的改变白边出现在最小生成树。这个东西显然可以二分。之后取一下最小值就可以了。

CODE

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
using namespace std;

struct Edge{
    int x, y, length, col;

    bool operator <(const Edge &a)const {
        if(length == a.length)
            return col > a.col;
        return length < a.length;
    }
    void Read() {
        scanf("%d%d%d%d", &x, &y, &length, &col);
        ++x, ++y;
        col ^= 1;
    }
}edge[MAX];

int points, edges, need;
int father[MAX];

int Find(int x) 
{
    if(father[x] == x)  return x;
    return father[x] = Find(father[x]);
}

inline pair<int, int> MST()
{
    for(int i = 1; i <= points; ++i)
        father[i] = i;
    pair<int, int> re(0, 0);
    sort(edge + 1, edge + edges + 1);
    for(int i = 1; i <= edges; ++i) {
        int fx = Find(edge[i].x), fy = Find(edge[i].y);
        if(fx != fy) {
            father[fy] = fx;
            re.second += edge[i].col;
            re.first += edge[i].length;
        }
    }
    return re;
}

int main()
{
    cin >> points >> edges >> need;
    for(int i = 1; i <= edges; ++i)
        edge[i].Read();
    int l = -110, r = 110, ans;
    while(l <= r) {
        int mid = (l + r) >> 1;
        for(int i = 1; i <= edges; ++i)
            edge[i].length += edge[i].col * mid;
        int white = MST().second;
        for(int i = 1; i <= edges; ++i)
            edge[i].length -= edge[i].col * mid;
        if(white >= need)
            l = mid + 1, ans = mid;
        else
            r = mid - 1;
    }
    for(int i = 1; i <= edges; ++i)
        edge[i].length += edge[i].col * ans;
    cout << MST().first - need * ans << endl;
    return 0;
}