Codeforce 1175 D. Array Splitting

新鲜热乎的题 Codeforce 1175 D

题意:给出一个长度为$n$的序列$a$,你需要把它划分为$k$段,每一个元素都需要刚好在其中一段中。分好之后,要计算$sum_{i=1}^{n} (a_i centerdot f(i))$,使这个值最大。其中$f(i)$代表第$i$个元素被分在第几段中。简单来说,就是每个元素被分在第几段中就需要乘上几,使序列的和最大。

假设$sum$是$a$的前缀和,$k$个分割点分别为$l_j$,将序列分割为$[1, l_1],[l_1 + 1, l_2]......[l_{k-1} + 1, l_k]$,那么第$j$段对总和的贡献是$(sum_{l_j} - sum_{l_{j-1}}) * j$。

由此,总和应该为$sum_{l_1} + (sum_{l_2} - sum_{l_1}) * 2 + (sum_{l_3} - sum_{l_2}) * 3+……+(sum_{l_k} - sum_{l_{k-1}}) * k$,很显然,相邻两项之间存在可以抵消的部分,同时$l_k$一定为$n$。将其化简:$sum_n centerdot k - sum_{l_1} - sum_{l_2} -……-sum_{l_{k-1}}$,那么我们只需要在前$n-1$项的前缀和中找出最小的$k-1$个就可以得到最大的总和了。

int n, k;
int a;
ll sum[maxn];
int main() {
    scanf("%d %d", &n, &k);
    priority_queue<ll, vector<ll>, greater<> > q;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a);
        sum[i] = sum[i - 1] + a;
        if(i != n)
            q.push(sum[i]);
    }
    ll ans = sum[n] * k;
    for(int i = 1; i <= k - 1; ++i) {//找出最小的k-1个前缀和
        ans -= q.top();
        q.pop();
    }
    printf("%lld
", ans);
}
原文地址:https://www.cnblogs.com/sun-of-Ice/p/10982844.html