CF2144D Price Tags 题解
思路
设 \(A = \max a_i\)。若 \(x > A\),则 \(x\) 的答案就等于 \(x-1\) 的答案,因为 \(\forall i \in [1,n], \lceil \displaystyle \frac{a_i}{x} \rceil = \lceil \displaystyle \frac{a_i}{x-1} \rceil\)。所以 \(x \le A \le 3 \times 10^5\)。但还需要考虑一次 \(x > A\) 的情况。此时所有的价格均为 \(1\),所以答案就是 \(n - (n - cnt_1) \times y\)。
先预处理 \(cnt_i\) 代表原序列 \(a\) 中 \(i\) 出现的个数。
我们考虑对于给定的 \(x\),如何快速算出答案。我们考虑每一个值 \(j\) 在 \(\lceil \displaystyle \frac{a_i}{x} \rceil\) 中出现了多少次。假设范围 \(\lceil \displaystyle \frac{r}{x} \rceil = j\)。显然,\(r\) 的取值范围为 \(r \in [j \times (x - 1) + 1, j \times x]\)。所以当 \(a_i \in [j \times (x - 1) + 1, j \times x]\) 时,就需要 \(cnt_i\) 个值为 \(j\) 的标签。即一共有
个值为 \(j\) 的价格。此时的答案即为:
\(num\) 预处理前缀和即可。
此时问题就为 \(j\) 应该如何枚举。我们会发现 \(j \le \lceil \displaystyle \frac{A}{x} \rceil\)。
我们算一下对于 \(1 \le x \le n\) 的 \(x\) 的时间复杂度。实际上即为:
我们发现这是调和级数,时间复杂度约为 \(\mathcal{O}(A \log A)\)。因为还有一个 \(\mathcal{O}(n)\) 的预处理,所以对于每一组数据,时间复杂度为 \(\mathcal{O}(n + A \log A)\)。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5;
int t, n, y;
int c[N], cnt[N], sum[N];
void solve()
{
scanf("%lld%lld", &n, &y);
int maxn = 0;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &c[i]);
maxn = max(maxn, c[i]);
cnt[c[i]]++;
}
for (int i = 1; i < N; i++)
sum[i] = sum[i - 1] + cnt[i];
int ans = n - (n - cnt[1]) * y;
for (int i = 2; i < maxn; i++)
{
int res = 0, tmp = (maxn + i - 1) / i;
for (int j = 1; j <= tmp; j++)
{
int num = sum[min(i * j, (int)2e5)] - sum[i * (j - 1)];
res += num * j - max(num - cnt[j], 0ll) * y;
}
ans = max(ans, res);
}
printf("%lld\n", ans);
for (int i = 1; i <= maxn; i++)
cnt[i] = sum[i] = 0;
}
signed main()
{
scanf("%lld", &t);
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号