• 【BZOJ2654】Tree


    题意

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

    分析

    第一想法是贪心,但是只考虑到贪心会让生成树可能不连通,于是开心地求了割边,把割边的顶点combine后再加白边,再加黑边。

    WA到只有10分。因为本质还是在贪心,只要贪心地把黑白边分开就不对

    比如只有三个顶点的一张图,有四条边,中间的一个顶点到另外两个顶点分别有一条白边一条黑边,此时限制只能加入一条白边。

    假如到左边点的白边权值为1,黑边权值为3,到右边点的白边权值为2,黑边权值为10.

    贪心,用左边的白边,那么不得不用右边的黑边,则sum=1+10=11>3+2=5.很明显贪心错误

    虽然先求割边再分黑白跑Kruskal这是错误做法,但也有一定启示。在真正求生成树的时候,似乎已经有一些边被加进来了,导致已经产生了一定值

    那么我们既然无法区分开黑白边,我们干脆忽略黑边过多的影响,仅使它们的权值对答案造成影响。

    即对于每条白边,加上一个mid值(正负皆可)来控制数量(改变白边的排列顺序),暂且可理解为因为有黑边的加入所造成的答案偏移值。而边权范围为-100~100,很明显,二分这个偏移值

    对于每次二分出来的答案,需要通过kruskal检验,如果用到的白边超过了限制,则需要加入更大的偏移值。

    对于边权相同的黑白边,要把颜色相同的放在一起,否则影响正确性(影响白边数量单调性)

    二分结束后的l不是答案!!需要跑一边kruskal带进去求出真正的sum,最后输出的答案要减去k条白边最后一次操作的偏移值

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cmath>
    #include<iostream>
    using namespace std;
    #define N 505000
    int n,m,k,ans,sum;
    int fa[N],first[N],ui[N],vi[N],wi[N],ci[N];
    struct email
    {
        int u,v,w,c;
    }e[N*10];
    inline void add(int u,int v,int w,int c,int p)
    {
        e[p].u=u;e[p].v=v;e[p].w=w;e[p].c=c;
    }
    bool cmp(email a,email b)
    {
        if(a.w!=b.w)
            return a.w<b.w;
        return a.c<b.c;
    }
    
    inline int find(int x)
    {    return fa[x]==x?x:fa[x]=find(fa[x]);}
                
    bool check(int mid)
    {
        int ans=0,num=0;    
        for(int i=0;i<n;i++)
            fa[i]=i;
        for(int i=1;i<=m;i++)
        {
            add(ui[i],vi[i],wi[i],ci[i],i);
            if(!ci[i])e[i].w+=mid;
        }
        sort(e+1,e+1+m,cmp);
        sum=0;
        for(int i=1;i<=m;i++)
        {
            int u=e[i].u,v=e[i].v,w=e[i].w,c=e[i].c;
            int fax=find(u),fay=find(v);
            if(fax!=fay)
            {
                fa[fax]=fay;
                num++;sum+=w;
                if(!c)ans++;
            }    
            if(num==n-1)
                break;
        }
        if(ans>=k)
            return true;
        return false;
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d%d",&ui[i],&vi[i],&wi[i],&ci[i]);
        int mid,l=-100,r=100;
        while(l<r)
        {
            mid=l+r+1>>1;
            if(check(mid))
                l=mid;
            else
                r=mid-1;
        }
        check(l);
        printf("%d
    ",sum-k*l);
        return 0;
    }
    “Make my parents proud,and impress the girl I like.”
  • 相关阅读:
    Kibana详细入门教程
    Python爬取食品商务网蔬菜价格数据,看看蔬菜最近的价格情况
    用Python爬取某蔬菜网的行情,分析底哪个地区的蔬菜便宜
    ES启动失败;java.lang.IllegalStateException: No factory method found for class org.apache.logging.log4j.c
    ELK 5.0 组件后台启动
    linux中/etc/security/limits.conf配置文件说明
    redis面试常见问题
    单线程的Redis为什么能支持10w+的QPS?
    Redis大Key优化
    redis中查找大key方法汇总
  • 原文地址:https://www.cnblogs.com/NSD-email0820/p/9440463.html
Copyright © 2020-2023  润新知