洛谷P1314
😟
🧾 题目背景(换种说法)
你是一个质监员,要对一批矿石进行抽检。
每块矿石有两个属性:
- 重量
w[i]
(比如:10 吨) - 价值
v[i]
(比如:1000 块)
现在给你一些检查的“区间”,比如第 2 块到第 5 块矿石(从第几块到第几块):
- 第一个区间是从第
l₁
块到第r₁
块 - 第二个区间是从第
l₂
块到第r₂
块 - …… 一共
m
个这样的区间
🎯 你的任务
你要选择一个 检查标准 W(重量阈值),然后计算每个区间中符合“重量 ≥ W”的矿石,它们的:
- 数量
- 价值和
对于每个区间 $[l, r]$,它的得分是:
这个区间内重量 ≥ W 的矿石个数 × 它们的总价值
比如:
- 假设在第 3 到第 6 个矿石中,有 2 块矿石重量 ≥ W,它们的价值分别是 100 和 200
- 那么这个区间的得分就是:2 × (100 + 200) = 2 × 300 = 600
对所有区间都这么算一遍,最后你会得到一个总分 y
📌 目标
你会被告诉一个期望的总分 S
你的目标是:
找一个重量标准 W,使得最终得分 y 尽可能接近 S(让 |y - S| 最小)
🧠 举个例子来理解
有 5 块矿石:
编号 | 重量 w[i] | 价值 v[i] |
---|---|---|
1 | 10 | 100 |
2 | 20 | 200 |
3 | 5 | 50 |
4 | 30 | 300 |
5 | 15 | 150 |
一个区间是 [2, 4](也就是第2~4块)
假设你选的重量阈值 W = 15,那么我们来看看 [2,4] 中谁重量 ≥ 15:
- 第 2 块:20 ≥ 15 → ✅(价值 200)
- 第 3 块:5 < 15 → ❌
- 第 4 块:30 ≥ 15 → ✅(价值 300)
→ 有两个符合的矿石,价值和 = 200 + 300 = 500
→ 得分是 2 × 500 = 1000
总结
你就是在“调一个参数 W”,希望让所有区间按“符合 ≥W 的矿石 × 价值”算出来的总分 y
,接近一个目标值 S
。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 200005;
ll n, m, S, ans = LLONG_MAX;
ll w[MAXN], v[MAXN], l[MAXN], r[MAXN];
ll cnt[MAXN], sum[MAXN];
void check(ll W) {
for(int i = 1; i <= n; i++) {
cnt[i] = cnt[i - 1] + (w[i] >= W);
sum[i] = sum[i - 1] + ((w[i] >= W) ? v[i] : 0);
}
ll y = 0;
for(int i = 1; i <= m; i++) {
ll c = cnt[r[i]] - cnt[l[i] - 1];
ll s = sum[r[i]] - sum[l[i] - 1];
y += c * s;
}
ans = min(ans, abs(S - y));
}
int main() {
cin >> n >> m >> S;
ll max_w = 0;
for(int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
max_w = max(max_w, w[i]);
}
for(int i = 1; i <= m; i++) {
cin >> l[i] >> r[i];
}
ll left = 0, right = max_w + 1;
while(left <= right) {
ll mid = (left + right) / 2;
check(mid);
ll y = 0;
for(int i = 1; i <= m; i++) {
ll c = cnt[r[i]] - cnt[l[i] - 1];
ll s = sum[r[i]] - sum[l[i] - 1];
y += c * s;
}
if(y > S) left = mid + 1;
else right = mid - 1;
}
cout << ans << '\n';
return 0;
}