qoj4239 MST

SOLUTION FROM WUMIN4

题意

给出 \(n\) 个整数 \(a_i\)。有一个 \(n\) 个点的无向完全图,定义 \(x,y\pod {x<y}\) 的边权为 \(a_y-a_x\),问这个图的最小生成树。

思路

完全图最小生成树,考虑 Boruvka 最小生成树算法。

具体的说,初始状态为 \(n\) 个单独的点,因此有 \(n\) 个连通块。

每次对每个连通块找到连接到另一个连通块的最小边权,全部找完后进行连接,若要连接的两个原连通块不在同一连通块中则连接。

随后递归进行以上操作,即可求出最小生成树。

由于每次规模缩小一半,所以复杂度是 \(O(n\log n)\) 的,前提是能快速找到连接另一连通块的最小边权。

对于 \(a_i\),显然在它的左边的最大 \(a_i\) 和在它右边的最小 \(a_i\) 可能成为连接它的最小边权。

于是我们对 \(i\) 维护在 \(i\) 左边的最大 \(a_i\) 和右边的最小 \(a_i\)且不在同一连通块中

可以用一个 set 存下每个连通块的最大最小值,若要求的最大最小值为该连通块,则后移一位即可。

代码

💩💩💩💩💩💩💩

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
int T,n,a[300005],f[300005],f2[300005],mx1[300005],mn1[300005],mnm[300005],mni[300005];
set<pair<int,int>> imx,imn;//最大值,连通块
int lmx[300005],lmn[300005];
int find(int x){
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
int find2(int x){
	if(f2[x]!=x) f2[x]=find2(f2[x]);
	return f2[x];
}
int boruvka(){//1 8
	for(int i=1;i<=n;i++)
		f[i]=i,f2[i]=i;
	int cnt=n,res=0;
	while(cnt!=1){
		imx.clear();
		for(int i=1;i<=n;i++)
			mnm[i]=inf,mni[i]=0,lmx[i]=0,mx1[i]=0,imx.insert({-inf,find(i)});
		a[0]=-inf;
		for(int i=1;i<=n;i++){
			mx1[i]=lmx[prev(imx.end())->second];
			if(find(mx1[i])==find(i)){
				if(imx.size()>1)
					mx1[i]=lmx[prev(prev(imx.end()))->second];
				else
					mx1[i]=0;
			}
			imx.erase({a[lmx[find(i)]],find(i)});
			if(a[i]>a[lmx[find(i)]])
				lmx[find(i)]=i;
			imx.insert({a[lmx[find(i)]],find(i)});
		}
		imn.clear();
		for(int i=1;i<=n;i++)
			lmn[i]=0,mn1[i]=0,imn.insert({inf,find(i)});
		a[0]=inf;
		for(int i=n;i>=1;i--){
			mn1[i]=lmn[imn.begin()->second];
			if(find(mn1[i])==find(i)){
				if(imn.size()>1)
					mn1[i]=lmn[next(imn.begin())->second];
				else
					mn1[i]=inf;
			}
			imn.erase({a[lmn[find(i)]],find(i)});
			if(a[i]<a[lmn[find(i)]])
				lmn[find(i)]=i;
			imn.insert({a[lmn[find(i)]],find(i)});
		}
		for(int vl,id,i=1;i<=n;i++){
			if(mx1[i]==0) id=mn1[i];
			else if(mn1[i]==0) id=mx1[i];
			else if(a[i]-a[mx1[i]]<=a[mn1[i]]-a[i]) id=mx1[i];
			else id=mn1[i];
			vl=(id<i?a[i]-a[id]:a[id]-a[i]);
			if(vl<mnm[find(i)]){
				mnm[find(i)]=vl;
				mni[find(i)]=id;
			}
		}
		for(int i=1;i<=n;i++)
			f2[i]=f[i];
		for(int i=1;i<=n;i++){
			if(mni[find(i)]==0) continue;
			int xx=find2(i),yy=find2(mni[find(i)]);
			if(xx!=yy){
				res+=mnm[find(i)];
				f2[xx]=yy;
				cnt--;
			}
			mni[find(i)]=0;
		}
		for(int i=1;i<=n;i++)
			f[i]=f2[i];
	}
	return res;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>a[i];
		cout<<boruvka()<<endl;
	}
	return 0; 
}
posted @ 2025-09-15 20:28  WuMin4  阅读(8)  评论(0)    收藏  举报