首页 > 其他分享 >Borůvka

Borůvka

时间:2024-11-09 19:08:02浏览次数:2  
标签:连通 val int Bor ans vka

详解

Borůvka 算法的本质是一种多路 Prim 最小生成树,复杂度 \(m\log n\),但劣于 Kruskal 的 \(\log\)

算法流程是这样的

考虑当前的图(未连边),一定由若干连通块构成,我们考虑连接连通块

可以想到,对于任意一个连通块,一定应该与尽可能优的连通块连边,并且,如果该连通块不在本操作连边,无论以后的操作如何改变其他连通块的状态,连通块总是单调不减的,花费也一定是单调不减的,因此直接在本操作连边即可

根据上述原理,可以尝试枚举所有边,然后尝试用这条边去更新端点所在连通块,对于连通块,则选择一个最优的边来更新自身

可以想到在一次操作中,总有至少一半的连通块被连接而消失,复杂度 \(m\log n\)

需要注意的有以下几点

  • “最优的边” 在这里必须是严格的,即你需要保证不存在 \(i,j\in [1,m]\),使得两条边 \(e_i=e_j\),至于为什么要这么做,一个经典的例子是重边,如果现在有 \((1,2,w=1)\) 和 \((2,1,w=1)\) 两条边,这两条边使得连通块 \(1\) 和连通块 \(2\) 都能被更新到,这样在合并的时候会连出环来,保证严格的最优性一般采用编号做第二关键字的办法
  • 当然,你在无向图上跑最小生成树也会连出环,这个时候的解决办法是你在第二个循环里合并连通块之前判一下

Borůvka 需要判的东西比较多,注意不要漏掉

P3366 【模板】最小生成树

#include<bits/stdc++.h>
using namespace std;
int n,m;
int best[100001];
struct edge{
    int from,to,w;
    int id;
    bool used;
    bool operator <(const edge&A)const{
        if(w==A.w) return id<A.id;
        return w<A.w;
    }
};
vector<edge>e={{0,0,0,0,true}};
struct dsu{
    int fa[100001];
    void clear(){
        for(int i=1;i<=n;++i){
            fa[i]=i;
        }
    }
    int find(int id){
        if(id==fa[id]) return id;
        return fa[id]=find(fa[id]);
    }
    bool samefa(int x,int y){
        int fx=find(x),fy=find(y);
        return fx==fy;
    }
    bool join(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx==fy) return false;
        fa[fx]=fy;
        return true;
    }
}d;
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int x,y,z;
        cin>>x>>y>>z;
        e.push_back({x,y,z,i,false});
    }
    d.clear();
    int ans=0,tot=0;
    while(1){
        bool flag=false;
        memset(best,0,sizeof best);
        for(edge i:e){
            if(i.used or d.samefa(i.from,i.to)) continue;
            int tmp=d.find(i.from);
            int tmp2=d.find(i.to);
            if(best[tmp]==0 or i<e[best[tmp]]){
                best[tmp]=i.id;
            }
            if(best[tmp2]==0 or i<e[best[tmp2]]){
                best[tmp2]=i.id;
            }
        }
        for(int i=1;i<=n;++i){
            if(d.find(i)==i){
                if(best[i]!=0 and e[best[i]].used==false){
                    e[best[i]].used=true;
                    ans+=e[best[i]].w;
                    tot++;
                    d.join(e[best[i]].from,e[best[i]].to);
                    flag=true;
                }
            }
        }
        if(!flag) break;
    }
    if(tot!=n-1) cout<<"orz";
    else cout<<ans;
}

使用

显然,Borůvka 在稠密图上的表现不如 Prim,在稀疏图上的表现不如 Kruskal

那要这玩意有什么用吗

是因为 Borůvka 适用于一类特殊题目

这类题目形如 给你一个完全图,完全图上的边权可以通过端点的点权经过某种计算得出,求最小生成树

这样的题充分利用了 Borůvka 只会合并 \(\log n\) 次的性质,这是其他两个最小生成树算法做不到的

但是这并不意味着你套模板就行了,暴力 Borůvka 仍然在 \(n^2\log\) 级别,需要一些有性质的图来优化算法(一般是快速找到最小边权)

星际联邦

完全图上每个点有点权 \(a_i\),定义 \((u,v)(u\lt v)\) 的边权为 \(a_v-a_u\),求最小生成树

(大概绿-蓝)

我们在每一轮需要找到这个点向外到另一个联通块内的最小边。注意到当 \(i\) 固定时,最小边要么是前缀 \([1, i)\) 的最大值取到的,要么是 \((i, n]\) 内的后缀最小值取到的。我们只需要对每个前缀维护最大值,以及和最大值不在同一个联通块内的最大值,后缀同理,就可以快速求出该联通块向外的最小边

时间复杂度为 \(O(n \log n)\)

#include<bits/stdc++.h>
using namespace std;
const long long inf=0x3f3f3f3f3f3f3f3f;
int n;
int a[300005];
struct val_t{
	long long val,pos;
	inline bool operator<(const val_t&A)const{
        return val<A.val;
    }
    inline bool operator>(const val_t&A)const{
        return val>A.val;
    }
	inline bool operator!=(const val_t&A)const{
        return pos!=A.pos;
    }
};
inline val_t max(val_t a,val_t b){
    return a>b?a:b;
}
inline val_t min(val_t a,val_t b){
    return a<b?a:b;
}
struct pairval_t{
    val_t x,y;
};
const val_t pos_inf={inf,0};
const val_t neg_inf={-inf,0};
inline pairval_t max(pairval_t a,pairval_t b){
	pairval_t ans={max(a.x,b.x),neg_inf};
	if(a.x!=ans.x and a.x>ans.y) ans.y=a.x;
	if(a.y!=ans.x and a.y>ans.y) ans.y=a.y;
	if(b.x!=ans.x and b.x>ans.y) ans.y=b.x;
	if(b.y!=ans.x and b.y>ans.y) ans.y=b.y;
	return ans;
}
inline pairval_t min(pairval_t a,pairval_t b){
	pairval_t ans={min(a.x,b.x),pos_inf};
	if(a.x!=ans.x and a.x<ans.y) ans.y=a.x;
	if(a.y!=ans.x and a.y<ans.y) ans.y=a.y;
	if(b.x!=ans.x and b.x<ans.y) ans.y=b.x;
	if(b.y!=ans.x and b.y<ans.y) ans.y=b.y;
	return ans;
}
struct pairval_t maxn[300001],minn[300001];
struct val_t val[300001];
inline val_t askmax(int x,int pos){
    return (maxn[x].x.pos==pos)?maxn[x].y:maxn[x].x;
}
inline val_t askmin(int x,int pos){
    return (minn[x].x.pos==pos)?minn[x].y:minn[x].x;
}
struct dsu{
    int fa[300001];
    inline void clear(){
        for(int i=1;i<=n;++i){
            fa[i]=i;
        }
    }
    int operator[](int id){
        if(id==fa[id]) return id;
        return fa[id]=this->operator[](fa[id]);
    }
}d;
inline long long Boruvka(){
	long long ans=0;
    d.clear();
	for(int i=1;i<=n;++i){
		maxn[i].x={a[i],i};
        maxn[i].y=neg_inf;
		minn[i].x={a[i],i};
        minn[i].y=pos_inf;
	}
	for(int i=2;i<=n;++i){
        maxn[i]=max(maxn[i-1],maxn[i]);
    }
	for(int i=n-1;i>=1;--i){
        minn[i]=min(minn[i+1],minn[i]);
    }
	while(1){
        bool flag=false;
		for(int i=1;i<=n;++i){
            val[i]=pos_inf;
        }
		for(int i=1;i<=n;++i){
			int now=d[i];
			val_t p=askmin(i,now);
            val_t q=askmax(i,now);
			if(q.val!=-inf and a[i]-q.val<=val[now].val){
                val[now]={a[i]-q.val,q.pos};
            }
			if(p.val!=inf and p.val-a[i]<=val[now].val){
                val[now]={p.val-a[i],p.pos};
            }
		}
		for(int i=1;i<=n;++i){
			if(d[i]==i){
                if(d[val[i].pos]==i or val[i].val==inf) continue;
                d.fa[d[val[i].pos]]=i;
                ans+=val[i].val;
                flag=true;
            }
		}
		for(int i=1;i<=n;++i){
			maxn[i].x={a[i],d[i]};
            maxn[i].y=neg_inf;
			minn[i].x={a[i],d[i]};
            minn[i].y=pos_inf;
		}
		for(int i=2;i<=n;++i){
            maxn[i]=max(maxn[i-1],maxn[i]);
        }
		for(int i=n-1;i>=1;--i){
            minn[i]=min(minn[i+1],minn[i]);
        }
        if(!flag) break;
	}
	return ans;
}
int main(){
    freopen("star.in","r",stdin);
    freopen("star.out","w",stdout);
	ios::sync_with_stdio(false);
    cin>>n;
	for(int i=1;i<=n;++i){
        cin>>a[i];
    }
	cout<<Boruvka();
}

CF888G Xor-MST

完全图上每个点有点权 \(a_i\),定义 \((u,v)(u\neq v)\) 的边权为 \(a_u \operatorname{xor} a_v\),求最小生成树

考虑放到 trie 树上维护异或和最值

码量还行,找个时间码了

(紫)

给定两颗带权无向树 \(T_1,T_2\),定义 \(dis_i(x,y)\) 表示树 \(T_i\) 上 \(x,y\) 间的距离,现有一完全二分图,左部,右部分别有 \(n\) 个点,定义左部点 \(i\) 与右部点 \(j\) 之间的边权为 \(\max\limits_{x=1}^n(dis_1(i,x)+dis_2(j,x))\),求完全二分图最小生成树

(chun chun ai si b)

https://h.hszxoj.com/d/hztg/contest/6716222721518607d314c04f/file/graph.cpp

标签:连通,val,int,Bor,ans,vka
From: https://www.cnblogs.com/HaneDaCafe/p/18536835

相关文章

  • Boredom
    TB.BoredomAlexdoesn’tlikeboredom.That’swhywheneverhegetsbored,hecomesupwithgames.Onelongwintereveninghecameupwithagameanddecidedtoplayit.Givenasequenceaconsistingofnintegers.Theplayercanmakeseveralsteps.In......
  • Vmware Workstation Pro出现不可恢复错误:NOT_IMPLEMENTED bora\lib\unicode\unicod
    该问题今天被我碰到了,百度搜索无果后在Google搜到了官方community也有国人抱怨这个问题,他指出17.6.1版本经常碰到这个问题,于是我一路退回退回到17.5.2版本就好了,估计这是新版本的bug。这个bug和一个utf8编码的库出现错误有关。参见:https://community.broadcom.com/vmware-cloud-f......
  • 配置docker和containerd,使用ca证书访问harbor
    配置docker和containerd,使用ca证书访问harbor目录配置docker和containerd,使用ca证书访问harbordocker配置ca证书访问harborcontainerd配置ca证书访问harbor验证证书有效性docker配置方法containerd配置方法验证证书有效性描述harbor链接汇总harbor部署harbor部署httpsdo......
  • Harbor双主复制高可用部署
    环境信息:主机名称IP备注harbor01192.168.61.56harbor1服务器harbor02192.168.61.57harbor2服务器192.168.61.59Nginx代理192.168.61.56/57两个节点分别部署docker-ce,docker-compose,harbor-offline-installer-v2.9.1.tgz部署docker-cedocker-com......
  • harbor 使用https部署 与 docker 登录
    目录配置harbor证书1.生成证书颁发机构证书及私钥2.生成服务器私钥及证书签名请求(CSR)3.生成证书签名请求4.生成x509v3扩展文件。5.使用该v3.ext文件为Harbor服务器生成证书。6.将test.harbor.com.crt转换为test.harbor.com.cert,供Docker使用。Docker守护进程将.crt......
  • 根据swagger.yaml生成harbor私库api调用代码
    准备下载https://github.com/goharbor/harbor/blob/main/api/v2.0/swagger.yaml下载https://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.4.43/swagger-codegen-cli-2.4.43.jar生成调用代码swagger-codegen-cli是用java写的,但是支持生成多种语言的调用代码,......
  • OpenCV(cv::copyMakeBorder())
    目录1.函数定义2.示例代码3.应用场景4.注意事项cv::copyMakeBorder()是OpenCV中用于给图像添加边框的函数,可以将指定宽度和类型的边框添加到图像的四周。这种操作在图像处理和计算机视觉任务中非常常见,比如在卷积运算中,通过填充边框来避免边界效应影响结果。1.函数......
  • Rust的Reborrow机制
    最近,在使用Rust时遇到了Reborrow的概念,记录下来以备以后参考。1.起因起因准备对数据进行Min-Max标准化处理,也就是将一系列数据映射到一个新的范围。首先,需要遍历数据,找出其中的最大值和最小值,然后通过公式改变原始数据集的值。Min-Max公式:标准化后的值=(原始值-最小值)/......
  • k8s 使用 containerd 作为容器运行时拉取 http 的 harbor 私有仓库镜像
    目录版本介绍报错内容解决方法主配置文件修改创建镜像仓库配置备注版本介绍k8s:v1.28.2containerd:1.6.33报错内容我的harbor用的是http的,因为是内网自己用,就没有配置https了,于是配置好镜像拉取的凭据,pod拉取镜像会有以下的报错Failedtopullimage"harbor.de......
  • 除了无界鼠标mouse without borders之外还有什么其他的软件可以两台电脑共享一套键鼠
    除了无界鼠标(MouseWithoutBorders)之外,还有几款常见的软件可以实现两台或多台电脑共享一套键盘和鼠标。以下是一些替代选择:1.Synergy平台支持:Windows、macOS、Linux特点:Synergy是一款非常流行的跨平台键鼠共享软件,允许你在多台电脑之间无缝切换。同样支持不同操作系统间......