洛谷P1314

P1314 [NOIP 2011 提高组] 聪明的质监员

😟


🧾 题目背景(换种说法)

你是一个质监员,要对一批矿石进行抽检。

每块矿石有两个属性:

  • 重量 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;
}
posted @ 2025-05-15 10:53  Chuan81  阅读(3)  评论(0)    收藏  举报