题目笔记——P8957 「CGOI-3」巫泡弹弹乐

题目大意

给定 \(n\) 个点,每个点有两个值:\(a_i\) 和 \(b_i\)。\(i\)、\(j\)两点之间有一条无向边边权为 \(\max(a_i,a_j)+\max(b_i,b_j)\),求这 \(n\) 个点组成的最小生成树。

解题思路

方法一:

将 \(n\) 个点以 \(a\) 从小到大进行排序后依次放入生成树,每次放入点与原本生成树连边的 \(a\) 绝对是当前这个点的 \(a\),计算出答案为 \(\sum\limits_{i=2}^na_i\),可以证明这为最优答案。

每次将放入点与原生成树中的最小值连边保证最优。

每次放入点有两种情况:

  • 放入点的 \(b\) 大于等于原生成树中的最小 \(b\):记一次放入点的 \(b\) 的答案,并再也不重复使用。
  • 放入点的 \(b\) 小于原生成树中的最小 \(b\):记一次原生成树中的最小 \(b\),并用放入点的 \(b\) 替换成原生成树中的最小 \(b\)。

由此,可以证明除序列 \(b\) 中的最小值以外,所有的都只会记一次答案,答案计为:\(\sum\limits_{i=2}^nb_i\),为最优答案。

因为 \(a\) 和 \(b\) 都为中最优答案,所以此方案为最优解(之一)。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 20;
struct node{
	int a, b, i;
}a[N];
int n, sum, mna = 1e18, mnb = 1e18;

bool cmp(node x, node y){return x.a < y.a;}

signed main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i].a;
		a[i].i = i;
		sum += a[i].a;
		mna = min(mna, a[i].a);
	}
	for(int i = 1; i <= n; i++){
		cin >> a[i].b;
		sum += a[i].b;
		mnb = min(mnb, a[i].b);
	}
	cout << sum - mna - mnb << endl;
	sort(a + 1, a + n + 1, cmp);
	for(int i = 2, mn = a[1].b, mni = 1; i <= n; i++){
		printf("%lld %lld\n", a[mni].i, a[i].i);
		if(a[i].b <= mn){
			mn = a[i].b;
			mni = i;
		}
	}
	return 0;
}

方法二:

依旧将 \(n\) 个点以 \(a\) 从小到大进行排序,此时对于任意 \(i\)、\(j\) 满足 \(j<i\),要么 \(b_j<b_i\),最优,要么 \(b_j>b_i\),那就也依次放入生成树,\(i\) 为当前要加入的节点,\(j\) 为当前生成树中 \(b\) 最小的下标,可以直接连接 \(i\)、\(j\)。

  • 当 \(b_j<b_i\) 时,此时 \(i\) 可以完全将 \(j\) 覆盖,答案增加 \(a_i+b_i\)。
  • 当 \(b_j>b_i\),用 \(i\) 替换掉 \(j\),答案增加 \(a_i+b_j\)。

最后输出答案和边就行了。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 20;
struct node{
	int a, b, i;
}a[N];
int n, ans;
vector<pair<int, int> > vec;

bool cmp(node x, node y){return x.a < y.a;}

signed main(){
	cin >> n;
	for(int i = 1; i <= n; i++) scanf("%lld", &a[i].a);
	for(int i = 1; i <= n; i++) scanf("%lld", &a[i].b);
	for(int i = 1; i <= n; i++) a[i].i = i;
	sort(a + 1, a + n + 1, cmp);
	for(int i = 2, mn = 1; i <= n; i++){
		vec.push_back({a[i].i, a[mn].i});
		if(a[i].b > a[mn].b) ans += a[i].a + a[i].b;
		else{
			ans += a[mn].b + a[i].a;
			mn = i;
		}
	}
	cout << ans << endl;
	for(pair<int, int> x : vec){
		printf("%lld %lld\n", x.first, x.second);
	}
	return 0;
}
posted @ 2026-10-27 16:30  show__code  阅读(4)  评论(0)    收藏  举报