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\) 的标签。即一共有

\[\begin{aligned} num = \sum_{i=j \times (x - 1) + 1}^{j \times x} cnt_i \end{aligned} \]

个值为 \(j\) 的价格。此时的答案即为:

\[num \times j - \max\{num - cnt_j, 0\} \times y \]

\(num\) 预处理前缀和即可。

此时问题就为 \(j\) 应该如何枚举。我们会发现 \(j \le \lceil \displaystyle \frac{A}{x} \rceil\)

我们算一下对于 \(1 \le x \le n\)\(x\) 的时间复杂度。实际上即为:

\[\begin{aligned} \sum_{x=1}^A \lceil \displaystyle \frac{A}{x} \rceil \end{aligned} \]

我们发现这是调和级数,时间复杂度约为 \(\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;
}
posted @ 2026-01-04 16:46  lucasincyber  阅读(2)  评论(0)    收藏  举报