这道题目让我受益匪浅啊!更让我认识到快读的 牛逼!!!acm生涯第一次用快读。
整整快了一倍!!!
Solution
对于简单版本的解法,就是利用优先队列求解。(可以先学习一下简单版本)
本题中由于数据量较大,需要O(n)的算法才可以。于是我们可以利用两个队列来模拟大根堆即可。
如何模拟呢?
- 首先利用桶排序对读入的数据排序,存入一个队列中(还有一个队列用来存放合并的数据)
- 每次取出两个队列中最小的两个数,然后合并放入第二个队列
- 第一个队列是有序的,第二个队列也一定是有序的嘛?是的,一定是有序的。
(粗暴的办法就是自己随机写十个数,模拟一下,就会恍然大雾) - 也可以简单分析一下,假如第二个队列不空,则取出两个当前最小的两个数然后合并,放入第二队列末尾。这个新数前面的数(假设取出两个数后,第二个队列里面还有数)肯定小于它,为什么呢? 因为它前面的数是“上次”最小的两个数合并而来的,这次的新数是“上次”第三小和第四小的两个数合并而来,所以新数一定小于它前面的数。
Code
#include<bits/stdc++.h>
#define endl "\n"
#define sz(a) a.size()
#define ll long long int
using namespace std;
const int N = 1e5 + 3;
int cnt[N];
queue<ll> q, qq;
int read()
{
int s = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
ll get_first()
{
ll x;
if (q.empty() && !qq.empty()) x = qq.front(), qq.pop();
else if (!q.empty() && qq.empty()) x = q.front(), q.pop();
else if (!q.empty() && !qq.empty())
if (q.front() < qq.front()) x = q.front(), q.pop();
else x = qq.front(), qq.pop();
else x = -1;
return x;
}
int main()
{
int n; cin >> n;
//桶排序
for (int i = 1, x; i <= n; i++)
x = read(), cnt[x]++;//快读加速
for (int i = 1; i <= 1e5; i++)
while (cnt[i]--) q.push(i);
ll ans = 0;
while (sz(q) + sz(qq) > 1)
{
ll x = get_first();
ll y = get_first();
ans += x + y;
qq.push(x + y);
}
cout << ans << endl;
return 0;
}