P5687 [CSP-S2019 江西] 网格图 题解

P5687 [CSP-S2019 江西] 网格图

Description

给定长为 \(n\) 的序列 \(a\) 和长为 \(m\) 的序列 \(b\),你需要按如下步骤生成一张 \(n\times m\) 的网格图,并求出这张图的最小生成树:

  • 对于所有 \(1\le i\le n\),在第 \(i\) 行的相邻两个点之间连上边权为 \(a_i\) 的边。

  • 对于所有 \(1\le i\le m\),在第 \(i\) 列的相邻(上下)两个点之间连上边权为 \(b_i\) 的边。

\(n,m\le 3\times 10^5\)

Solution

考虑 Kruskal 的概念,求这张网格图的最小生成树就是在不形成环的前提下连边权最小的 \(n\times m -1\) 条边,这个边数过于庞大。

不过我们又注意到每一列(或是一行)的边权都是相等的。也就是说如果你钦定了一行为最小,就可以把这一整行都连上。不过还得思考一下判环的方法。什么时候会出现环?当点 \((x,y)\) 的所在行和列都被连上了边(当前在处理第 \(x\) 行或第 \(y\) 列)。

我们发现如果当前在考虑第 \(x\) 列,最多可以加 \(n\) 条边;而此前已连了 \(y\) 个横行,可以去掉 \(y-1\) 条边,因此只加了 \(n-y+1\) 条边,考虑第 \(y\) 列同理。

然后就获得了 100pts。

#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,m,tot,ans,edg;
struct node{
	int typ,val;
}a[600005];
inline bool cmp(node x,node y){
	return x.val<y.val;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n+m;i++){
		cin>>a[++tot].val;
		if(i<=n){
			a[tot].typ=1;
		}
		else{
			a[tot].typ=2;
		}
	}
	sort(a+1,a+1+tot,cmp);
	int cnt1=0,cnt2=0;
	for(int i=1;i<=tot;i++){
		if(a[i].typ==1){
			cnt1++;
			ans+=(m-1)*a[i].val;
			if(cnt1>1&&cnt2>1){
				ans-=(cnt2-1)*a[i].val;
			}
		}
		else{
			cnt2++;
			ans+=(n-1)*a[i].val;
			if(cnt1>1&&cnt2>1){
				ans-=(cnt1-1)*a[i].val;
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2025-08-05 23:18  Creativexz  阅读(29)  评论(0)    收藏  举报