题目描述
你有若干个[1,n] 内的正整数:对于 1≤i≤n,你有 ai 个整数 i。设S=∑i=1nai。
对于一个序列 p1,p2,⋯,pl,定义其众数 maj(p1,p2,⋯,pl) 为出现次数最多的数。若有多个数出现次数最多,则其中最大的数为其众数。
现在你需要把这 S 个数排成一个序列 b1,b2,⋯,bS,使得∑i=1Smaj(b1,b2,⋯,bi) 最大。输出该最大值。
输入格式
第一行一个整数 n,表示值域。
接下来一行 n 个正整数 a1,a2,⋯,an,表示每种数的个数。
输出格式
输出一行一个正整数表示 ∑i=1Smaj(b1,b2,⋯,bi) 的最大值。
输入输出样例
输入 #1
3 1 3 2
输出 #1
17
考虑最终序列的形式。一个自然的想法是不断从 n摆到 1,如果没有这个数就跳过。
正确性证明:考虑众数序列 m1∼mS,根据众数定义,其中只能有不超过 ∑i=1nmin(ai,an) 个 n,只能有不超过∑i=1nmin(ai,max(an−1,an)) 个n−1 或 n。以此类推,只能有不超过 ∑i=1nmin(ai,maxj=pnaj) 个不小于 p 的数。而不断从 n 摆到 1 的序列恰好达到了所有上界。□□
接下来考虑贡献。考虑一个数 i 被第 j 次放入序列,那么它对应的众数为最大的 �p 使得 ap≥j。因此,设 fj 表示最大的 p 使得 ap≥j,则 ai 的贡献为 ∑j=1aifj。前缀和即可。
时间复杂度 O(n+a)。
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 5;
long long n, ans, a[N], f[N];
int main() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = n, mx = 0; i; i--) {
while(mx < a[i]) mx++, f[mx] = f[mx - 1] + i;
ans += f[a[i]];
}
cout << ans << "\n";
return 0;
}