NC14291 Cut

题目链接

题目

题目描述

给你一个长度为 \(n\) 的序列,你每次可以将一个序列分割成两个连续的的子序列,
分割的代价为原序列的总和。
现在允许你在初始时将序列重新排列一次。
问分割成 \(n\) 个长度为 \(1\) 的序列的最大总代价是多少?

输入描述

第一行一个数 \(n\) 表示原序列的长度;
接下来一行 \(n\) 个数 \(a_i\) 表示原序列的第 \(i\) 个数。
\(2<=n<=100000\)
\(0<=a_i<=10000\)

输出描述

一行一个整数表示答案。

示例1

输入

4
3 2 4 1

输出

26

说明

[3,2,4,1]重排->[1,2,3,4]->[1],[2,3,4]->[1],[2],[3,4]->[1],[2],[3],[4]。

示例2

输入

4
1 1 1 1

输出

9

题解

方法一

知识点:贪心,数学。

显然,每次把最小的取出来,大的加的机会就更多。

不妨再进一步思考,实际上从小到大取,第 \(i\) 个数会加 \(i\) 次。特别地,最后一个数只有 \(n-1\) 次,因为最后一次没有取。

所以有 \(ans = \sum_{i=1}^n i \cdot a_i - a_n\)

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

方法二

知识点:贪心,优先队列。

贪心思路还是一样的,不过这次用优先队列傻瓜式维护即可。

不过需要注意的是,为了方便我们假设一个等价情形,元素还未合并,要求每次合并两段序列,使得最后花费最多。显然是从大的先合并,答案和从小开始的分割序列等价。于是,很容易用优先队列维护。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int a[100007];

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> a[i];
    sort(a + 1, a + 1 + n);
    ll ans = -a[n];
    for (int i = 1;i <= n;i++) {
        ans += 1LL * i * a[i];
    }
    cout << ans << '\n';
    return 0;
}

方法二

#include <bits/stdc++.h>
#define ll long long


using namespace std;

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    priority_queue<ll> pq;
    for (int i = 0, tmp;i < n;i++) cin >> tmp, pq.push(tmp);
    ll ans = 0;
    for (int i = 0;i < n - 1;i++) {
        ll sum = pq.top();
        pq.pop();
        sum += pq.top();
        pq.pop();
        pq.push(sum);
        ans += sum;
    }
    cout << ans << '\n';
    return 0;
}
posted @ 2022-07-08 23:08  空白菌  阅读(89)  评论(1)    收藏  举报