• bzoj2654 Tree


    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

     

    二分+最小生成树,一开始真的很难想。就是二分一个值,给所有的白色边加上这个值,跑最小生成树保证白边数刚好是need

    其中二分中的细节需要注意一下。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <queue>
    #include <stack>
    #include <vector>
    using namespace std;
    #define MAXN 100010
    #define INF 2147483647
    #define MOD 10000007
    #define LL long long
    #define in(a) a=read()
    #define REP(i,k,n) for(int i=k;i<=n;i++)
    #define DREP(i,k,n) for(int i=k;i>=n;i--)
    #define cl(a) memset(a,0,sizeof(a))
    inline int read(){
        int x=0,f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
        return x*f;
    }
    inline void out(int x){
        if(x<0) putchar('-'),x=-x;
        if(x>9) out(x/10);
        putchar(x%10+'0');
    }
    int n,m,need;
    int sum,num;
    int ans;
    int f[MAXN];
    int a[MAXN],b[MAXN],w[MAXN],c[MAXN];
    struct node{
        int a,b,w,c;
    }l[MAXN];
    int getf(int k){
        if(f[k]==k)  return k;
        return f[k]=getf(f[k]);
    }
    bool cmp(node x,node y){
        if(x.w==y.w)  return x.c<y.c;
        return x.w<y.w;
    }
    bool check(int k){
        sum=0;
        num=0;
        REP(i,1,n)  f[i]=i;
        REP(i,1,m){
            l[i].a=a[i];
            l[i].b=b[i];
            l[i].c=c[i];
            l[i].w=w[i];
            if(l[i].c==0)  l[i].w+=k;
        }
        sort(l+1,l+m+1,cmp);
        for(int i=1;i<=m;i++){
            int x=getf(l[i].a),y=getf(l[i].b);
            if(x!=y){
                f[y]=x;
                sum+=l[i].w;
                if(!l[i].c)  num++;
            }
        }
        if(num>=need)  return true;
        return false;
    }
    int main(){
        in(n);in(m);in(need);
        REP(i,1,m){
            in(a[i]);in(b[i]);in(w[i]);in(c[i]);
            a[i]++;
            b[i]++;
        }
        int left=-10010,right=10010;
        while(left<=right){
            int mid=(right+left)>>1;
            if(check(mid)){
                left=mid+1;    
                ans=sum-mid*need;
            }
            else right=mid-1;
        }
        cout<<ans;
        return 0;
    }
     
  • 相关阅读:
    .NET ------ 多线程的简单使用
    .NET --- 页面刷新(html 和 js两种方式)
    .NET ---- B/S的特点,不接收js赋值
    二分查找与二分答案
    c++运行程序 鼠标点击按钮 (c++)(windows)
    c++运行程序 光标隐藏与移动 (c++)(windows)
    推荐:史蒂芬霍金论天道
    LaTeX公式学习
    Markdown语法学习
    文言语言!!!(附c/c++自译)
  • 原文地址:https://www.cnblogs.com/jason2003/p/9636891.html
Copyright © 2020-2023  润新知