zoukankan      html  css  js  c++  java
  • WQS二分入门 & LuoguP2619 Tree I

    题意

    传送门

    题意简化: 给定一个带权无向图,边分为黑白两色,求白边数量为k时的最小生成树

    (k<=n<=5*10^4, m<=10^5)

    Solution

    WQS二分

    此题作为WQS的入门题, 先得讲讲WQS二分

    先谈谈自己的感性理解:

    WQS二分主要用于: 消除求解最值问题中的物品个数限制

    主要思想: 通过对于每个物品加固定权值通过最大(最小)的约束,不断逼近(达到)要求的物品个数限制

    使用前提: 对于物品选取个数 (X_i), 所对应的答案 (Y_i) 需要满足( (X_i,Y_i) ) 能在平面上组成凸包

    通常对于凸性的证明或寻找,可采取以下几种方式:

    1. 暴力打表
    2. 感性理解(即:物品选的越多/块数分的越多,答案必定越大/越小)

    建议在有了感性认知之后,能有理性的对于算法本质的理解

    推荐博客:https://blog.csdn.net/a_forever_dream/article/details/105581221

    LuoguP2619 Tree I

    对于此题而言
    若我们将每条选的白边都加上某个权值,则BST排序后白边会靠后
    即: 选的白边数量变少,且黑边内部与白边内部边权相对关系并不发生变化

    所以考虑二分附加权值,每次将白边-附加权,在当前的最小生成树中统计白边数量cnt
    若 cnt>=k: 则说明此时白边选多了, 需要增大附加权
    否则: 减小附加权

    最后再在答案中将附加权消除即可

    code

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define in inline
    #define get getchar()
    #define ll long long
    in int read()
    {
        int t=0; char ch=get;
        while(ch<'0' || ch>'9') ch=get;
        while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
        return t;
    }
    const int _=1e5+23;
    struct edge{
        int u,v,val;
    }b[_],w[_],e[_];
    int tot1,tot2,n,m,lim,vis[_],fa[_];
    in int cmp(edge a,edge b)
    { return a.val<b.val; }
    in int find(int x)
    { return fa[x]==x ? fa[x] : fa[x]=find(fa[x]);}
    in int check(int x,int &sum)
    {
        int s1=1,s2=1;
        // 此题黑白边相对顺序不会改变,可以归并排序
        for(re int i=1;i<=m;++i) 
        {
            if(w[s2].val+x<=b[s1].val) e[i]=w[s2],vis[i]=1,e[i].val+=x,s2++;
            else e[i]=b[s1],vis[i]=0,s1++;
            if(s2>tot2) {
                for(re int j=s1;j<=tot1;++j) ++i,e[i]=b[j],vis[i]=0;
                break;
            }
            if(s1>tot1) {
                for(re int j=s2;j<=tot2;++j) ++i,e[i]=w[j],vis[i]=1,e[i].val+=x;
                break;
            }
        }
        int used=0;
        for(re int i=1;i<=n;++i) fa[i]=i;
        for(re int i=1;i<=m;++i)
        {
            int u=e[i].u, v=e[i].v;
            int fx=find(u), fy=find(v);
            if(fx==fy) continue;
            fa[fx]=fy;sum+=e[i].val;
            used+=vis[i];
        }
        return used;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
    #endif 
        n=read(), m=read(), lim=read();
        for(re int i=1;i<=m;++i)
        {
            int x=read()+1, y=read()+1, z=read(), o=read();
            if(o==1) b[++tot1].u=x, b[tot1].v=y, b[tot1].val=z;
            else w[++tot2].u=x, w[tot2].v=y, w[tot2].val=z;
        }
        sort(b+1,b+tot1+1,cmp), sort(w+1,w+tot2+1,cmp);
        int l=-120, r=120,k,ans;
        while(l<=r)
        {
            int mid=l+r>>1,sum=0;
            int used=check(mid,sum);
            if(used>=lim) l=mid+1, ans=sum-lim*mid; 
            // 注意这里是大于等于时就要统计答案,即可能在凸包上出现三点共线
            else r=mid-1;
        }
        cout<<ans<<endl;
    }
    
    嗯,就这样了...
  • 相关阅读:
    录制游戏视频——fraps
    ssh 带端口登录
    You have new mail in /var/spool/mail/root 烦不烦你?
    php生成xml的四种方法(转)
    liunx命令之whereis、which、find的区别和联系
    wancms从apache迁移至nginx
    面试题
    NP
    Careercup | Chapter 7
    OS | 哲学家问题
  • 原文地址:https://www.cnblogs.com/yzhx/p/14617211.html
Copyright © 2011-2022 走看看