P1314 [NOIP2011 提高組] 聰明的質監員

洋洋萱發表於2024-11-18

題目

[NOIP2011 提高組] 聰明的質監員

題目描述

小T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n 個礦石,從 1 到 n 逐一編號,每個礦石都有自己的重量 wi 以及價值 vi 。檢驗礦產的流程是:

  1. 給定m 個區間 [li,ri];
  2. 選出一個引數 W;
  3. 對於一個區間 [li,ri],計算礦石在這個區間上的檢驗值 yi:

其中 j 為礦石編號。

這批礦產的檢驗結果 y 為各個區間的檢驗值之和。即:

若這批礦產的檢驗結果與所給標準值 s 相差太多,就需要再去檢驗另一批礦產。小T 不想費時間去檢驗另一批礦產,所以他想透過調整引數 W 的值,讓檢驗結果儘可能的靠近標準值 s,即使得 |s-y|最小。請你幫忙求出這個最小值。

輸入格式

第一行包含三個整數 n,m,s,分別表示礦石的個數、區間的個數和標準值。

接下來的 n 行,每行兩個整數,中間用空格隔開,第 i+1 行表示 i 號礦石的重量 wi 和價值 vi。

接下來的 m 行,表示區間,每行兩個整數,中間用空格隔開,第 i+n+1 行表示區間 [li,ri] 的兩個端點 li 和 ri。注意:不同區間可能重合或相互重疊。

輸出格式

一個整數,表示所求的最小值。

樣例 #1

5 3 15 
1 5 
2 5 
3 5 
4 5 
5 5 
1 5 
2 4 
3 3 

輸出 #1

10

題解

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>

const int N = 2e5 + 10;
long long n, m, goal;  
int w[N], v[N], l[N], r[N];  // w 為礦石的重量,v 為礦石的價值,l 和 r 為區間的起始和結束位置
long long a[N], s[N];  // a 是儲存字首和的陣列,s 是儲存字首和的礦石價值陣列

using namespace std;

// 檢查給定 W 值時的檢驗結果
long long check(int mid);

int main() {
    cin >> n >> m >> goal;
    
    // 輸入礦石的重量和價值
    for(long long i = 1; i <= n; i++) {
        cin >> w[i] >> v[i];
    }

    // 輸入區間的起始和結束位置
    for(long long i = 0; i < m; i++) {
        cin >> l[i] >> r[i];
    }

    // 二分查詢的右邊界是 w 的最大值 + 1
    long long l = 0, r = 1e6 + 1;
    
    while(l < r) {
        long long mid = l + r + 1 >> 1;
        if(check(mid) >= goal) {
            l = mid;  // 說明當前 W 值可以滿足條件,繼續嘗試更大的 W
        } else {
            r = mid - 1;  // 如果不能滿足目標值,嘗試更小的 W
        }
    }

    // 輸出最接近目標的結果,檢查 l 和 r 對應的檢驗值差值
    cout << min(abs(check(r) - goal), abs(check(r + 1) - goal)) << endl;
}

// 計算給定 W 值時的檢驗值
long long check(int mid) {
    long long y = 0;

    // 更新字首和陣列
    for(long long i = 1; i <= n; i++) {
        if(w[i] >= mid) {
            a[i] = a[i - 1] + 1;  // 礦石重量大於等於 W,數量 +1
            s[i] = s[i - 1] + v[i];  // 礦石價值加上
        } else {
            a[i] = a[i - 1];
            s[i] = s[i - 1];
        }
    }

    // 計算所有區間的檢驗值
    for(long long i = 0; i < m; i++) {
        // 根據字首和計算每個區間的檢驗值
        long long count = a[r[i]] - a[l[i] - 1];  // 區間內滿足條件的礦石數量
        long long value_sum = s[r[i]] - s[l[i] - 1];  // 區間內滿足條件的礦石的價值和
        y += count * value_sum;  // 計算檢驗值並累加
    }

    return y;
}

相關文章