题目题意

我们有n种种子,每种种子有c个且每种种子中每个都为w重,然后每个种子算作一堆,假设一共有m堆种子,我们的任意一次操作可以使任意两堆种子合并为一堆,直至仅剩下一堆,即执行m-1堆,且代价值=合并前两堆种子重量之和,即合并后的重量,要求计算出代价值之和的最小值。

题目知识点

贪心、模运算、数据结构

题目分析

这题让我们通过安排合并种子的顺序,从而使代价值总和最小。

那么我们可以想象一下,如果让最大值不论与最小值还是那个进行合并,得到的代价值只会比最大值还要大,所以,我们自然就能想到只有去合并不断更新后的堆里面的两个最小值,才能使代价值归为最小。

而且不同种子的重量有可能相同,我们可以用一个map来合并相同重量的种子,这样会避免超过空间限制

我们可以将相同重量的种子们一次性先进行合并,从而避免超时。

有了这样一个思路后,我们便可以写代码了:

先录入数据并合并相同重量的种子

	int n;
	ll sum = 0, ans = 0;    //sum用来计算总共有多少堆种子,ans用来计算答案
	map<ll, ll>mp;          //map来合并相同重量的种子
	cin >> n;
	for (int i = 1; i <= n; i++) {
		ll c, w;
		cin >> c >> w;     //录入数据
		mp[w] += c;        
		sum += c;
	}

然后到了最核心的部分:

	while (sum > 1) {
		auto [w1, c1] = *mp.begin();
		mp.erase(mp.begin());
		if (c1 >= 2) {       //该if用来一次性合并相同重量的种子
			mp[2 * w1] += c1 / 2;
			sum -= c1 / 2;
			ans = (ans + (2 * w1 % mod) * (c1 / 2 % mod)) % mod;  //更新ans
			if (c1 % 2==0) continue;  //如果该重量的种子个数为偶数,一定会被合并完毕,那么就直接考虑下一个重量
		}
        if(mp.empty()) break;
		auto [w2, c2] = *mp.begin();
        mp[w1 + w2]++;      //将合并后的重量加一
        ans = (ans + w1 + w2) % mod;   //更新ans
        mp[w2]--;
		if (!mp[w2]) mp.erase(mp.begin());  //如果w2重量的种子被合并完后,就被剔除
		sum--;
	}

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
void solve() {
	int n;
	ll sum = 0, ans = 0;
	map<ll, ll>mp;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		ll c, w;
		cin >> c >> w;
		mp[w] += c;
		sum += c;
	}
	if (sum == 1) {
		cout << "0" << endl;
		return;
	}
	while (sum > 1) {
		auto [w1, c1] = *mp.begin();
		mp.erase(mp.begin());
		if (c1 >= 2) {
			mp[2 * w1] += c1 / 2;
			sum -= c1 / 2;
			ans = (ans + (2 * w1 % mod) * (c1 / 2 % mod)) % mod;
			if (c1 % 2==0) continue;
		}
        if(mp.empty()) break;
		auto [w2, c2] = *mp.begin();
        mp[w1 + w2]++;
        ans = (ans + w1 + w2) % mod;
        mp[w2]--;
		if (!mp[w2]) mp.erase(mp.begin());
		sum--;
	}
	cout << ans << endl;
}
int main() {
	int t = 1;
	while (t--) {
		solve();
	}
}