首页 > 其他分享 >WQS二分

WQS二分

时间:2022-11-14 18:03:24浏览次数:49  
标签:二分 int WQS mid mw 权值 need 100

用于求解具有单调性的从一堆物品中恰好选need个且满足权值最大/小化等要求的问题。

无向带权连通图,每条边是黑色或白色。求一棵最小权的恰好有need条白色边的生成树。二分给白边加上的权值,加的权值越大,最小生成树中白边数量越少,明显具有单调性。

#include<bits/stdc++.h>

using namespace std;

const int N=1e5+5;
struct edge{
    int x,y,w,c;
    inline friend bool operator<(const edge&a,const edge&b){
        return a.w==b.w?a.c<b.c:a.w<b.w;//优先选白边
    }
}e[N];
int f[N],x[N],y[N],w[N],c[N],tot,cnt,n,m,need,sum;
inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
inline bool check(int k){
    tot=cnt=0;
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=m;i++)e[i]={x[i],y[i],w[i]+(c[i]^1)*k,c[i]};//白边,加上k
    sort(e+1,e+1+m);//kruscal最小生成树
    for(int i=1;i<=m;i++){
        int a=find(e[i].x),b=find(e[i].y);
        if(a!=b){
            f[a]=b;
            tot+=e[i].w;
            if(!e[i].c)cnt++;//有一条白边被选到了生成树里面
        }
    }
    return cnt>=need;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m>>need;
    for(int i=1;i<=m;i++){
        cin>>x[i]>>y[i]>>w[i]>>c[i];
        x[i]++;y[i]++;//点是从0开始编号的
    }
    int l=-100,r=100;
    while(l<=r){
        int mid=l+r>>1;//二分给白边加上的权值
        if(check(mid))l=mid+1,sum=tot-need*mid;
        else r=mid-1;
    }
    cout<<sum;
    return 0;
}

写法上一个优化,将白边与黑边分开排序,这样在跑最小生成树时不用再次排序,用类似归并排序的方法扫一遍即可

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+9,M=1e6+9;
struct Edge{
	int x,y,w;bool c;
	inline bool operator<(const Edge&x)const{return w<x.w;}
}e[M];
int f[N],n,m,need;
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline bool add(int i){//尝试加边并返回是否成功
	if(find(e[i].x)==find(e[i].y))return 0;
	f[f[e[i].x]]=f[e[i].y];return 1;
}
int main(){
    cin>>n>>m>>need;
	int mw=0,i,j;
	for(i=0;i<m;++i){//点和边都从0开始存了
		cin>>e[i].x>>e[i].y>>e[i].w>>e[i].c;
		if(!e[i].c)swap(e[mw++],e[i]);//将黑白边分开
	}
	sort(e,e+mw);sort(e+mw,e+m);
	int l=-100,r=100,mid,ans;
	while(l<r){//神奇的二分函数
		mid=(l+r+1)>>1;
		for(i=0;i<n;++i)f[i]=i;
		ans=i=0;j=mw;
		while(i<mw&&j<m)e[i].w+mid<=e[j].w?ans+=add(i++):add(j++);//类归并排序
		while(i<mw)ans+=add(i++);//白边数量统计完整
		ans<need?r=mid-1:l=mid;
	}
	mid=l;
	for(i=0;i<n;++i)f[i]=i;
	ans=i=0;j=mw;//最后算权值总和
	while(i<mw&&j<m)if(e[i].w+mid<=e[j].w)ans+=(e[i].w+mid)*add(i),++i;else ans+=e[j].w*add(j),++j;
	while(i<mw)ans+=(e[i].w+mid)*add(i),++i;
	while(j<m )ans+=e[j].w*add(j),++j;
	cout<<(ans-need*mid);//减掉
	return 0;
}

标签:二分,int,WQS,mid,mw,权值,need,100
From: https://www.cnblogs.com/safeng/p/16889816.html

相关文章

  • 整体二分
    整体二分是将所有操作离线下来,对多组询问同时进行二分。将所有操作按照时间顺序存入数组,设[st,ed]为答案的定义域,即询问的区间,[l,r]为答案的值域。二分答案的值域,对于每......
  • 二分查找
    二分查找法实际上是不断地将有序数据进行对半分割,并检查每个分区的中间元素。再重复根据中间数确定目标范围并递归实行对半分割,直到中间数等于待查找的值或是目标数不在......
  • 洛谷P8849 『JROI-7』hibernal 二分法题解
    题目链接题目大意:交互题,给定N=2或18或64或512或1000,其中你要通过19次以内的询问在数列1-N中找到给定的未知的两个数x和y(本题解中设x<y),每次询问......
  • 20221112_T1A+_整体二分背包
    题意给定一个树,有\(q\)个询问,每次都是其子树内做背包。题解赛时得分:100/100子树,我们不难想到用dfs序上操作,那么现在问题变成了区间背包。区间背包怎么做,首先,对于......
  • 发现了二分查找的秘密
    **二分查找(BinarySearch)**算法,也叫折半查找算法。1.1、原理分析二分查找是一种非常简单易懂的快速查找算法,其思想在生活中随处可见,比如朋友聚会的时候爱玩的一个猜数游......
  • 自定义函数二分法查找,数组问题
    intfind(intarr1[],intx,inty){intleft=0;intright=y-1;while(right>=left){if(x>arr1[(left+right)/2])left=(left+right)/2+1;elseif(x<arr1[(l......
  • 道长的算法笔记:二分图匹配
    二分图的概念二分图又称作二部图,是图论中的一种特殊模型。假设\(G=(V,E)\)是一个无向图,如果顶点\(V\)能够分割为两个互不相交的子集\((S,T)\),并且图中的每条边\((......
  • 【题解】CF1485C Floor and Mod(二分答案,整除分块)
    【题解】CF1485CFloorandModemmm……NOIP考前两周,跟CSP考前一样(虽然最后并没有去考),写篇题解增加以下RP(雾)。提供一篇思路大体和题解区相同但用了二分写法的题解。......
  • ABC 270 E - Apple Baskets on Circle(二分)
    https://atcoder.jp/contests/abc270/tasks/abc270_e题目大意:有n个篮子排列成一个圆圈,每个篮子里面有ai个苹果......
  • 二分法
    三、二分法1.整数二分的本质:将整个区间一分为二。在这两个区间,选择一个一定能保证里面有答案的区间,区间代码模板如下。并且解决左右端点更新的问题程序中不要同时出现......