zoukankan      html  css  js  c++  java
  • Luogu P2619 [国家集训队2]Tree I

    传送门>>

    给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。

    这个算法叫..WQS二分/带权二分/DP凸优化

    用来解决一种特定类型的问题:

    n个物品,选择每一个都会有相应的费用,需要求出强制选need个物品时的最大/最小费用。

    适用范围:设$f(x)$为选$x$个物品的费用,$f(x)$为凸函数。

    并不会证明 感性理解一下:

    对于这道题,跑一遍最小生成树,如果选择的白边不够,就要增加白边的优先级。

    在kruskal算法中,选择每条边的优先级即为这条边的权值(越小越好)。

    那么,把每条白边的权值减少一个值x,再跑一边最小生成树。

    如果选择的白边多了,就要减少白边的优先级。

    用二分答案来枚举这个增加的值x。


    不知道为什么新开一个变量记录x会WA...

    完整代码如下

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #define MogeKo qwq
    #include<algorithm>
    using namespace std;
    const int maxn = 1e5+10;
    int n,m,k,x,y;
    int cnt,sum;
    int fa[maxn];
    
    struct edge {
        int u,v,val,col;
        bool operator < (const edge & N)const {
            return val<N.val || (val==N.val && col<N.col);
        }
    } e[maxn];
    
    int getfa(int x) {
        if(fa[x] == x)return x;
        return fa[x] = getfa(fa[x]);
    }
    
    void init() {
        for(int i = 1; i <= n; i++)
            fa[i] = i;
    }
    
    void kruskal() {
        cnt = sum = 0;
        init();
        sort(e+1,e+m+1);
        int ecnt = 0;
        for(int i = 1; i <= m; i++) {
            int uu = getfa(e[i].u);
            int vv = getfa(e[i].v);
            if(uu == vv) continue;
            fa[uu] = vv;
            sum += e[i].val;
            cnt += e[i].col^1;
            if(++ecnt == n-1)break;
        }
    }
    
    int BS() {
        int ans = 0;
        int l = -105,r = 105,mid;
        while(l <= r) {
            mid = (l+r)>>1;
            for(int i = 1; i <= m; i++)
                if(e[i].col == 0) e[i].val += mid;
            kruskal();
            if(cnt >= k) {
                ans = sum-(mid*k);
                l = mid+1;
            } else r = mid-1;
            for(int i = 1; i <= m; i++)
                if(e[i].col == 0) e[i].val -= mid;
        }
        return ans;
    }
    
    int main() {
        scanf("%d%d%d",&n,&m,&k);
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d%d",&x,&y,&e[i].val,&e[i].col);
            e[i].u = ++x;
            e[i].v = ++y;
        }
        printf("%d",BS());
        return 0;
    }
    View Code

     

     

  • 相关阅读:
    AWTK-MVVM 在 STM32H743 上的移植笔记
    windows 中文 unicode 编码显示
    SpringBoot项目jar包运行
    Activiti中的互斥网关、并行网关、兼容网关、事件网关
    【LeetCode】739.每日温度(5种方法,详细图解)
    【LeetCode】20.有效的括号(使用栈,动图详解)
    你知道权限管理的RBAC模型吗?
    关闭Win10自动更新
    iOS 中如何判断当前是2G/3G/4G/5G/WiFi
    GCD API 记录 (三)
  • 原文地址:https://www.cnblogs.com/mogeko/p/10993014.html
Copyright © 2011-2022 走看看