luogu5687 网格图 题解

分析

直接建图跑最小生成树只有 \(64pts\)

注意到对于一个节点 \((i,j)\),同在第 \(i\) 行的节点向它们的右边节点连边的代价都是 \(a_i\),同在 \(j\) 列的节点向它们的下方节点连边的代价都是 \(b_j\)。那么把 \(\{a\}\)\(\{ b\}\) 递增排序,此时就相当于把网格图交换了行与列。

这时候 \((1,1)\) 既对应着最小的 \(a_1\),又对应着最小的 \(b_1\),那么第一行与第一列都是要选择的,否则一定不是最小的。同时也可以推广到对于一个 \(a_i\)\(b_j\),要么不连,要么能连的连起来,才能保证最优性。

最小生成树不能有环。画图不难发现,在第一行和第一列都被选择的情况下,如果在格子图中出现了环(格子图中的最简单环是个正方形),那么一定存在 \((i,j)\),在某个时刻(不关心先后顺序)既选择了所有 \(a_i\),又选择了所有 \(b_j\),其中 \(i,j\) 均不为 1。为了防止出现这种情况,已经考虑过的部分不能被后面的决策影响。

所以就很明确了,维护变量 \(row\) 记录当前行,\(col\) 记录当前列,维护指针 \(p1\) 表示 \(a_{p1}\)\(p2\) 表示 \(b_{p2}\)

\(a_{p1} \le b_{p2}\) 时,连起来这一行能连的边,前 \(col\) 列已经使用过了,贡献为 \(a_{p1} \cdot (m-col)\),这一行不能再考虑,\(a_{p1}\) 不能再使用,\(row+1\)\(p1+1\)。反之贡献为 \(b_{p2} \cdot (n-row)\)\(col+1\)\(p2+1\)

\(p1>n\) 或者 \(p2> m\) 的时候,图已经连通,也就求出了最小生成树。

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e5+5;
int n, m;
ll a[N], b[N], ans;
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	for(int i=1;i<=m;++i) scanf("%lld",&b[i]);
	sort(a+1,a+n+1), sort(b+1,b+m+1);
	
	ans+=a[1]*(m-1)+b[1]*(n-1);
	int row=1, col=1, c1=2, c2=2;
	while(c1<=n&&c2<=m) {
		if(a[c1]<=b[c2]) ans+=a[c1++]*(m-col), ++row;
		else ans+=b[c2++]*(n-row), ++col; 
	}
	printf("%lld\n",ans);
}

luogu5687 网格图 题解
https://yozora0908.github.io/2022/lg5687=solution/
作者
yozora0908
发布于
2022年6月22日
许可协议