Skip to content

P8148 声海 Sea of Voices

约 594 字大约 2 分钟

排序

2025-03-06

题面

题解

注意到aia_i是单调不降的,那么很显然a1a_1a2a_2可重集SS中最小的两个。因此,a1a_1a2a_2很容易得到,那么进一步考虑a3a_3,发现在删去a1a_1a2a _2后,a3a_3可能是最小的。因为无法确定a3a_3a1+a2a_1+a_2的大小关系,那么很自然可以想到,如果再将a1+a2a_1+a_2删去,a3a_3就为最小的了。

以此类推,再已知a1a_1aka_k时,将其能计算出的结果从SS中删去,那么最小的就为ak+1a_{k+1}了。按照这个思路,先将SS排序,然后暴力维护能够删除的结果,能够得到50分。

考虑优化,发现这个过程有点像筛法。都是从一堆数中取出符合条件的数,并且都能通过已经得到的结果来对剩余的数进行筛选。我们要保留的数是aia_i要筛除的数是j=lraj\sum_{j=l}^{r} a_j,注意到ai105a_i\le10^5,因此我们只需要筛除j=lraj105\sum_{j=l}^{r} a_j\le10^5,我们可以开一个桶来处理。

这个桶有以下特点:

  1. 当一个数出现时对应的桶+1+1
  2. 当找到一个aia_i时,将这个aia_i能生成的所有后缀和所对应的桶都1-1
  3. 一个桶如果非负,那么这个桶对应的数为aia_i

这个桶就相当于相当于一个标记数组,我们通过操作标记数组来对原数据进行筛除。需要注意的是这里在筛除数据时使用的是后缀和而非前缀和,举个例子:

a3a_3筛除的是:

a3a2+a3a1+a2+a3a_3\\ a_2+a_3\\ a_1+a_2+a_3

a4a_4筛除的是:

a4a3+a4a2+a3+a4a1+a2+a3+a4a_4\\ a_3+a_4\\ a_2+a_3+a_4\\ a_1+a_2+a_3+a_4

很明显,后缀和能够不重复不遗漏的将需要筛除的数计算出来

#include<iostream>
#include<algorithm>
#include<cstdio>
#define N 2005
#define M 4000005
using namespace std;
int n,m,s[M];
int a[N],cnt;
int t[100005];
int main(){
	scanf("%d",&n);
	m=n*(n+1)/2;
	for(int i=1;i<=m;i++) scanf("%d",&s[i]);
	sort(s+1,s+m+1);
	for(int i=1;i<=m;i++){
		t[s[i]]++;
		if(t[s[i]]>0){
			a[++cnt]=s[i];
			if(cnt==n) break;
			int sum=0;
			for(int i=cnt;i>=1;i--){
				sum+=a[i];
				if(sum>100000) break;
				t[sum]--;
			}
		}
	}
	for(int i=1;i<=n;i++)
		printf("%d ",a[i]);
	puts("");
	return 0;
}