zoukankan      html  css  js  c++  java
  • BZOJ2654 tree 【二分 + 最小生成树】

    题目

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

    输入格式

    第一行V,E,need分别表示点数,边数和需要的白色边数。
    接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

    输出格式

    一行表示所求生成树的边权和。
    V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

    输入样例

    2 2 1

    0 1 1 1

    0 1 2 0

    输出样例

    2

    题解

    又是一个神奇的解法
    解法似乎很合理,,但又不知如何证明

    假如我们直接求一次最小生成树,白边的数量是无法预知的
    但有一点是肯定的:随着白边边权的减小,最小生成树中的白边数量增加
    我们就可以二分白边改变的边权,检验最终生成树中白边的数量是否>=need【黑白边权相同时优先选白,保证结果白边尽可能多】
    由于题目保证有解,所以最小的不小于need的方案就是最终结果
    最后ans要减去need * 白边加的权值

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 100005,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int pre[maxn],n,m,need,ans;
    struct EDGE{int a,b,v,c;}e[maxn];
    inline bool operator <(const EDGE& a,const EDGE& b){
    	return a.v == b.v ? a.c < b.c : a.v < b.v;
    }
    int find(int u){return u == pre[u] ? u : pre[u] = find(pre[u]);}
    bool check(int x){
    	for (int i = 1; i <= m; i++)
    		if (!e[i].c) e[i].v += x;
    	sort(e + 1,e + 1 + m);
    	for (int i = 1; i <= n; i++) pre[i] = i;
    	int fa,fb,cnt = n,tot = 0; ans = 0;
    	for (int i = 1; i <= m && cnt > 1; i++){
    		fa = find(e[i].a); fb = find(e[i].b);
    		if (fa != fb){
    			pre[fb] = fa;
    			cnt--; ans += e[i].v;
    			if (!e[i].c) tot++;
    		}
    	}
    	ans -= need * x;
    	for (int i = 1; i <= m; i++)
    		if (!e[i].c) e[i].v -= x;
    	if (tot >= need) return true;
    	return false;
    }
    int main(){
    	n = read(); m = read(); need = read();
    	for (int i = 1 ; i <= m; i++){
    		e[i].a = read() + 1; e[i].b = read() + 1;
    		e[i].v = read(); e[i].c = read();
    	}
    	int l = -100,r = 100,mid;
    	while (l < r){
    		mid = l + r + 1 >> 1;
    		if (check(mid)) l = mid;
    		else r = mid - 1;
    	}
    	check(l);
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    BrowserSync,自动刷新,解放F5,去掉更新提示
    js获取手机系统语言
    块元素,行内元素,行内块区别
    原生js判断某个元素是否有指定的class名的几种方法
    如何实现调用console.log(‘good’.repeat(3))时输出goodgoodgood?
    数组如何去除重复数据,只保留一条
    Sentinel笔记-Flow流控规则
    sentinel笔记 NodeSelectorSlot,ClusterBuilderSlot
    Sentinel笔记--Slotchain
    Sentinel笔记-核心类
  • 原文地址:https://www.cnblogs.com/Mychael/p/8524610.html
Copyright © 2011-2022 走看看