P12413 「YLLOI-R1-T2」圣诞星 题解

P12413 「YLLOI-R1-T2」圣诞星 题解

思路

  1. 首先,我们先考虑没有计算优惠券贡献的情况:此时最少钱数即为 \(\sum\limits_{i=1}^na_i\)
  2. 然后再考虑未购买优惠券的情况:
    由题知每购买完一个商品便会获得一个优惠券,并且优惠券是永久性的,即购买完该商品后,后续购买的所有商品都会收到该优惠券的影响。由于 \(a_i\) 较小的商品无法更多的利用优惠券,所以为了让优惠券发挥最大作用,我们需将 \(a_i\) 较大的商品放在后面购买,因为优惠券只对 \(a_i\ge1\) 的商品有贡献,那么我们可以对 \(a_i\) 从小到大排序,并减去前面 \(i-1\) 个优惠券的贡献。
  3. 最后考虑如何购买优惠券:
    这时应考虑优惠券如何对答案造成贡献,设 \(f(x)\) 为商品 \(a_i\ge x\) 的数量。
    模拟一下不难发现它的贡献为每次购买时商品 \(f(1)-w\),由于商品只有 \(n\) 个,若 \(w>n\),只要购买优惠券肯定就是不优的。
    那我们只需要考虑 \(w\le n\) 时的情况,易知在该情况下,若购买时 \(f(1)\ge w\),则可购买优惠券。我们将此过程放到全局来看,若想购买 \(k\) 张优惠券且有贡献,则需满足 \(f(k)\ge w\)。不难发现当 \(f(k)\ge w\) 时,\(f(k-1)\ge w\) 也必然满足,所以贡献值单调递增。所以当 \(f(k)=w\) 时,为 \(k\) 的上限。我们可对修改后 \(a_i\) 再次排序,方便查找 \(f(k)=w\) ,即 \(k=a_{n-w}\) 的情况。

注意事项:千万不要忘了 \(a_i\) 下限是 \(0\)

代码

\(\text{Code}\)

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

int n, w, num;
int a[N]; 
long long ans, sum;

template <typename type>
void read(type &res) {
	type x = 0, f = 1;
	char c = getchar();
	for (; c < 48 || c > 57; c = getchar()) if (c == '-') f = ~f + 1;
	for (; c > 47 && c < 58; c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	res = f * x;
}

int main() {
	read(n), read(w);
	for (int i = 1; i <= n; i++) read(a[i]); // 分析 2 中的第一次排序
	sort(a + 1, a + 1 + n); 
	for (int i = 2; i <= n; i++) a[i] = max(a[i] - (i - 1), 0), sum += a[i]; // 减去前 i - 1 个优惠券
	if (w > n) return printf("%lld", sum + a[1]), 0; // 不贪小便宜,原价更好
	sort(a + 1, a + 1 + n); // 第二次排序以方便查找 f(k) = w 的情况
    ans += 1ll * a[n - w] * w; // 加上优惠券的价格
    for (int i = 1; i <= n; i++) ans += max(0, a[i] - a[n - w]); // 减去优惠券的优惠
    printf("%lld", ans);
	return 0;
}
posted @ 2025-05-24 23:56  ZaleClover  阅读(37)  评论(0)    收藏  举报