专栏文章

题解:P14242 [CCPC 2024 Shandong I] 分割序列

P14242题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minkvdul
此快照首次捕获于
2025/12/02 04:04
3 个月前
此快照最后确认于
2025/12/02 04:04
3 个月前
查看原文

思路

首先我们把公式展开来看:
i=1ki×si=i=1ki×j=ri1+1riaj\sum_{i=1}^{k} i \times s_i = \sum_{i=1}^{k} i \times \sum_{j=r_{i-1}+1}^{r_i} a_j
我们把求和顺序调换一下,可以发现每个元素 aja_j 的贡献取决于它所在的段编号,也就是被乘上的系数是多少。也就是说,每个元素 aja_j 的系数等于它所属段的编号。
那么我们要做的事情就是:如何给每个元素分配段编号,使得整体加权和最大。
我们从右往左考虑,当我们从右往左切割时,每多切一刀就会让右边一段的系数增加 11。因此,如果一个“后缀的和”是正的,那把它放到右边能增加整体和;反之,如果后缀和是负的,就不想让它被更高的系数乘上,即不想让它太靠右。
于是问题转化成了我们需要知道每个后缀的和,并选择最大的几个后缀加到结果里。
具体而言,我们定义后缀和数组 suf[i]=ai+ai+1++an\text{suf}[i] = a_i + a_{i+1} + \dots + a_n 并且令 suf[n+1]=0\text{suf}[n+1] = 0。显然,suf[1]\text{suf}[1] 就是整段的总和,也就是 k=1k=1 的答案。当我们增加一个分段时,相当于把一个“后缀”独立出来乘以更大的系数。那我们就应该选择最大的后缀和来提升整体值。所以我们把所有后缀和(除了第一个)按从大到小排序。然后依次累加,得到每个 kk 的答案,这样就能得到所有的 v1,v2,,vnv_1, v_2, \dots, v_n

Code

CPP
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+10;
int n,a[N],suf[N];
signed main(){
	int T;cin>>T;
	while(T--){
		cin>>n;
		memset(suf,0,sizeof suf);
		for(int i=1;i<=n;i++)cin>>a[i];
		for(int i=n;i>=1;i--)suf[i]=suf[i+1]+a[i];
		sort(suf+2,suf+n+1,greater<int>());int v=suf[1];
		for(int i=1;i<=n;i++)cout<<v<<" ",v+=(i<n)*suf[i+1];
		cout<<"\n";
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...