• Luogu P2619 [国家集训队2]Tree I(WQS二分+最小生成树)


    P2619 [国家集训队2]Tree I

    题意

    题目描述

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

    题目保证有解。

    输入输出格式

    输入格式:

    第一行(V,E,need)分别表示点数,边数和需要的白色边数。

    接下来(E)

    每行(s,t,c,col)表示这边的端点(点从(0)开始标号),边权,颜色((0)白色(1)黑色)。

    输出格式:

    一行表示所求生成树的边权和。

    输入输出样例

    输入样例#1:

    2 2 1
    0 1 1 1
    0 1 2 0
    

    输出样例#1:

    2
    

    说明

    (0:V<=10)

    (1,2,3:V<=15)

    (0,..,19:V<=50000,E<=100000)

    所有数据边权为([1,100])中的正整数。

    (By WJMZBMR)

    思路

    (WQS)二分真的强。

    定义一个东西(delta),把它加在所有白边的边权上。对原图直接跑最小生成树,如果白边少了,就调小(delta);反之,则调大(delta)。最后得到答案。

    证明并不会,只会感性理解(毕竟我是蒟蒻)。安利一篇博客好了:关于WQS二分算法以及其一个细节证明 --Creeper_LKF

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=5e4+4,MAXM=1e5+5;
    int n,m,ned,ans,tot,fa[MAXN];
    struct Edge
    {
        int u,v,d,col;
        bool operator < (const Edge &sjf) const
        {
            if(d!=sjf.d) return d<sjf.d;
            return col<sjf.col;
        }
    }edge[MAXM];
    int read()
    {
        int re=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
        return re;
    }
    int fd(int x)
    {
        int r=x;
        while(r!=fa[r]) r=fa[r];
        int i=x,j;
        while(i!=r) j=fa[i],fa[i]=r,i=j;
        return r;
    }
    bool check(int lzq)
    {
        for(int i=0;i<m;i++) if(!edge[i].col) edge[i].d+=lzq;
        for(int i=0;i<n;i++) fa[i]=i;
        int white=0,cnt=1;tot=0;
        sort(edge,edge+m);
        for(int i=0;i<m;i++)
        {
            int x=edge[i].u,y=edge[i].v;
            if(fd(x)==fd(y)) continue;
            fa[fd(x)]=fd(y),cnt++,tot+=edge[i].d,white+=(!edge[i].col);
            if(cnt==n) break;
        }
        for(int i=0;i<m;i++) if(!edge[i].col) edge[i].d-=lzq;
        return white>=ned;
    }
    int main()
    {
        n=read(),m=read(),ned=read();
        for(int i=0;i<m;i++) edge[i].u=read(),edge[i].v=read(),edge[i].d=read(),edge[i].col=read();
        int L=-15000,R=15000;
        while(L<=R)
        {
            int M=(L+R)>>1;
            if(check(M)) L=M+1,ans=tot-M*ned;
            else R=M-1;
        }
        printf("%d",ans);
        return 0;
    }
    
  • 相关阅读:
    2014-9-思杨的缺点
    2014-8-9月杂记
    2014-7-31 思杨在老家想我们
    生日。金鼎轩吃饭;亿旺中影看《后会无期》。
    2014-7-5~6 秦皇岛之旅,带思杨看大海
    2014-6-23 去未来幼儿园玩————————
    2014-6-15 思杨的成长是全家人的责任。
    2014-6-14 思杨跳抓钱舞---疾驰的扭扭车和思杨司机
    2014-6-7 带思杨去西三旗儿童乐园玩-----思杨晕车了
    springboot学习之一
  • 原文地址:https://www.cnblogs.com/coder-Uranus/p/9904037.html
Copyright © 2020-2023  润新知