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;
}

浙公网安备 33010602011771号