alt 题目如上

题解:

题目理解

我们有 n 把三角尺,每把尺子有两条直角边 x 和 y。其中 y 边可以打磨(缩短),打磨总量不能超过 w。打磨后斜边长度会变小,目标是让所有尺子斜边长度总和最小。

思路 1. 问题转化

每把尺子的斜边长度公式:

斜边 = √(x² + y²)

打磨就是减小 y,从而减小斜边长度。

关键观察:随着打磨量增加,斜边长度减少的幅度(边际收益)会越来越小。

2. 贪心策略

想象一下:

初始时,每把尺子都没打磨

我们有 w 次"打磨机会",每次可以选一把尺子打磨 1 单位

每次应该选择打磨后斜边缩短最多的那把尺子

3. 实现方法

用优先队列(大根堆)来维护:

堆里放 (当前打磨收益, 尺子编号)

收益 = 现在打磨 1 单位能减少的斜边长度

每次弹出收益最大的尺子打磨

打磨后重新计算这把尺子的新收益,放回堆里

AC码如下(AI辅助完成,非完全独立完成):

void solve() {
	int n, w, i;
	cin >> n >> w;
	vi a(n), b(n), nw(n);
	ld sum = 0;

	priority_queue<pair<ld, int>> pq;

	for (i = 0; i < n; i++) {
		cin >> a[i] >> b[i];
		nw[i] = b[i];
		sum += sqrtl((ld)a[i] * a[i] + (ld)b[i] * b[i]);

		if (b[i] > 0) {
			ld cur = sqrtl((ld)a[i] * a[i] + (ld)b[i] * b[i]);
			ld next = sqrtl((ld)a[i] * a[i] + (ld)(b[i] - 1) * (b[i] - 1));
			ld gain = cur - next;
			pq.push({gain, i});
		}
	}

	ll cnt = 0;
	while (!pq.empty() && cnt < w) {
		auto [gain, idx] = pq.top();
		pq.pop();
		cnt++;

		sum -= gain;
		nw[idx]--;

		if (nw[idx] > 0) {
			ld cur = sqrtl((ld)a[idx] * a[idx] + (ld)nw[idx] * nw[idx]);
			ld next = sqrtl((ld)a[idx] * a[idx] + (ld)(nw[idx] - 1) * (nw[idx] - 1));
			ld new_gain = cur - next;
			pq.push({new_gain, idx});
		}
	}

	cout << fixed << setprecision(10) << sum;
}