[THUPC 2023 初赛] 众数

P9143 [THUPC 2023 初赛] 众数

题目描述

你有若干个 [ 1 , n ] [1,n] [1,n] 内的正整数:对于 1 ≤ i ≤ n 1 \leq i \leq n 1in,你有 a i a_i ai 个整数 i i i。设 S = ∑ i = 1 n a i S = \sum_{i=1}^n a_i S=i=1nai

对于一个序列 p 1 , p 2 , ⋯   , p l p_1,p_2,\cdots,p_l p1,p2,,pl,定义其众数 maj ( p 1 , p 2 , ⋯   , p l ) \text{maj}(p_1,p_2,\cdots,p_l) maj(p1,p2,,pl) 为出现次数最多的数。若有多个数出现次数最多,则其中最大的数为其众数。

现在你需要把这 S S S 个数排成一个序列 b 1 , b 2 , ⋯   , b S b_1,b_2,\cdots,b_S b1,b2,,bS,使得 ∑ i = 1 S maj ( b 1 , b 2 , ⋯   , b i ) \sum_{i=1}^S \text{maj}(b_1,b_2,\cdots,b_i) i=1Smaj(b1,b2,,bi) 最大。输出该最大值。


题解

设当前满足 a i ≠ 0 a_i\neq 0 ai=0的最大的 i i i值为 k k k,则对于 1 ≤ i ≤ k 1\leq i\leq k 1ik,我们可以让尽量多的 i i i值放入序列,且 i i i的数量不超过 a k a_k ak。那么此时答案中 a k a_k ak出现的次数为 ∑ i = 1 k min ⁡ ( a i , a k ) \sum\limits_{i=1}^k\min(a_i,a_k) i=1kmin(ai,ak)。每个位置也要减去对应的值,即 a i = a i − min ⁡ ( a i , a k ) a_i=a_i-\min(a_i,a_k) ai=aimin(ai,ak)

那么,对于所有的 i i i a i a_i ai可以分成若干段,每一段对应不同 a a a值的贡献。我们可以 O ( n ) O(n) O(n)求出每一段的长度并用队列维护。

用桶来存每个 a i a_i ai可取到的值,根据队列的每一段来统计其贡献。

可以根据代码来帮助理解。时间复杂度为 O ( n + m x a ) O(n+mxa) O(n+mxa),其中 m x a mxa mxa a i a_i ai的最大值。

code

#include<bits/stdc++.h>
using namespace std;
int n,mx,vk=1,a[100005],v[100005],z[100005];
long long ans=0;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);++z[a[i]];
		mx=max(mx,a[i]);
	}
	v[++v[0]]=n;
	for(int i=n-1;i>=1;i--){
		if(a[i]>a[v[v[0]]]){
			v[++v[0]]=i;
		}
	}
	for(int i=mx;i>=1;i--) z[i]+=z[i+1];
	for(int i=1;i<=mx;i++){
		ans+=1ll*z[i]*v[vk];
		if(i==a[v[vk]]) ++vk;
	}
	printf("%lld",ans);
	return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值