• 「一本通 3.1 练习 4」Tree 题解


    题目地址

    分析

    第一眼看到此题,感觉就是一道水题,直接加上前(need)小的白边就行了,再处理到(n-1)条黑边,但是,打完后突然发现有问题。。。 虽然加上了前(need)小的白边,但是会出现树不连通的现象,即无法构成生成树

    正解思路

    二分一个增量(mid)(可正可负)。
    跑一遍(Kruskal),将所有的白边都加上(a),记录构成生成树后所用到的白边,如果数量小于(need)就将右端点往左移,否则往右移。
    最后的(ans)需要减去增量(need * mid)

    代码

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int MAXN = 5 * 1e4 + 5;
    const int MAXM = 1e5 + 5;
    
    int n, m, need, fa[MAXN], l, r, mid, ans, cnt;
    
    struct node {
        int u, v, w, color;
    } dis[MAXM];
    bool cmp(node x, node y) {
        if (x.w == y.w)
            return x.color < y.color;
        return x.w < y.w;
    }
    
    int FindSet(int v) {
        if (fa[v] == v)
            return v;
        else {
            return fa[v] = FindSet(fa[v]);
        }
    }
    bool UnionSet(int u, int v) {
        int x = FindSet(u);
        int y = FindSet(v);
        if (x == y)
            return 0;
        fa[x] = fa[y];
        return 1;
    }
    
    bool check(int mid) {
        int tot = 0, white = 0;
        cnt = 0;
        for (int i = 0; i <= n; i++) fa[i] = i;
        for (int i = 1; i <= m; i++) {
            if (dis[i].color == 0) {
                dis[i].w += mid;
            }
        }
        sort(dis + 1, dis + 1 + m, cmp);
        for (int i = 1; i <= m; i++) {
            if (UnionSet(dis[i].u, dis[i].v)) {
                tot++;
                cnt += dis[i].w;
                if (dis[i].color == 0)
                    white++;
            }
            if (tot == n - 1)
                break;
        }
        for (int i = 1; i <= m; i++) {
            if (dis[i].color == 0) {
                dis[i].w -= mid;
            }
        }
        if (white < need) {
            return 0;
        } else
            return 1;
    }
    
    int main() {
        scanf("%d %d %d", &n, &m, &need);
        for (int i = 1; i <= m; i++) {
            scanf("%d %d %d %d", &dis[i].u, &dis[i].v, &dis[i].w, &dis[i].color);
        }
        l = -1e2 - 5, r = 1e2 + 5;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (check(mid)) {
                l = mid + 1;
                ans = cnt - need * mid;
            } else {
                r = mid - 1;
            }
        }
        printf("%d", ans);
        return 0;
    }
    
  • 相关阅读:
    C#线程优先级浅析
    Android常用组件
    Android 内存监测工具 DDMS --> Heap
    Android 十个非常漂亮的常用组件
    RelativeLayout 相对布局 常用属性
    Android 关于横竖屏
    (转)Android 之 StrictMode 介绍
    Android如何获取SIM卡信息
    Android 读SIM卡信息
    Android Camera 使用小结
  • 原文地址:https://www.cnblogs.com/cqbz-ChenJiage/p/13514249.html
Copyright © 2020-2023  润新知