题目描述
你有若干个 [ 1 , n ] [1,n] [1,n] 内的正整数:对于 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n,你有 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 1≤i≤k,我们可以让尽量多的 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=1∑kmin(ai,ak)。每个位置也要减去对应的值,即 a i = a i − min ( a i , a k ) a_i=a_i-\min(a_i,a_k) ai=ai−min(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;
}