BZOJ2654 tree (wqs二分)

Posted ynhnxn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ2654 tree (wqs二分)相关的知识,希望对你有一定的参考价值。

题目描述

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
 
一个最小生成树问题,但是我们要选need条白边,我们用g(i)表示选取i条白边的最优方案(生成树的权值最小),那么可以大致猜出g(i)是关于i的一个下凸函数,可以发现斜率k是有单调性的,我们二分这个斜率k,相当于给每条白边的权值加上一个k,统计数量use,如果use>=need,说明k小了,要增大,同理,use<need,要减小k。

那么问题来了,如果说当前白边加上mid后,白边条数use>need了,如果加上mid+1后,use<need了要怎么办?

题目中说到了:保证有解,所以出现上述情况时一定有黑边==白边的边权

所以我们只需要把一条黑边换成白边就好,即我们排序时如果黑边权值等于白边,则白边优先。

 #include<bits/stdc++.h>
 using namespace std;
 #define N 50001
 #define M 100001
 int n,m,k;
 struct node
     int u,v,w,c;
 e[M];
 int use,sum,fa[N];
 bool cmp(node a,node b)
     if(a.w!=b.w) return a.w<b.w;
     return a.c<b.c;//黑边白边权值一样,优先选白边 
 
 int find(int x)
     return fa[x]==x?x:fa[x]=find(fa[x]);
  
 void check(int x)
     for(int i=1;i<=m;i++)
         if(e[i].c==0) e[i].w+=x;//白边加权值 
     sort(e+1,e+m+1,cmp);
     for(int i=1;i<=n;i++) fa[i]=i;
     int now=0,fu,fv,j=1;
     use=0,sum=0;
     while(now!=n-1)
         fu=find(e[j].u);fv=find(e[j].v);
         if(fu!=fv)
             now++;
             fa[fu]=fv;
             if(e[j].c==0) ++use;//统计使用的白边数量 
             sum+=e[j].w;
         
         ++j;
     
     for(int i=1;i<=m;i++)
         if(e[i].c==0) e[i].w-=x;//还原 
 
 
 int main()
 
     scanf("%d%d%d",&n,&m,&k);
     for(int i=1;i<=m;i++)
         scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].c);
         ++e[i].u;++e[i].v;//题目是编号从0开始,要+1 
     
     int l=-101,r=101,mid,ans;//值域[1,100]; 
     while(l<=r)
         mid=l+r>>1;check(mid);
         if(use>=k)
             ans=sum-k*mid;
             l=mid+1;
         
         else r=mid-1;
     
     cout<<ans;
     return 0;
 

 

以上是关于BZOJ2654 tree (wqs二分)的主要内容,如果未能解决你的问题,请参考以下文章

带权二分

二分+最小生成树bzoj2654: tree

BZOJ2654tree 二分+最小生成树

BZOJ 2654 tree(二分答案+并查集)

BZOJ 2654 & 玄学二分+MST

bzoj 2654 tree