1. 程式人生 > >Luogu P1314 聰明的質監員

Luogu P1314 聰明的質監員

P1314 聰明的質監員

難度 提高+/省選-
時空限制 1000ms / 128MB

題目描述

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

1 、給定m 個區間[Li,Ri];

2 、選出一個引數 W;

3 、對於一個區間[Li,Ri],計算礦石在這個區間上的檢驗值Yi:
這裡寫圖片描述

這批礦產的檢驗結果Y 為各個區間的檢驗值之和。即:Y1+Y2…+Ym

若這批礦產的檢驗結果與所給標準值S 相差太多,就需要再去檢驗另一批礦產。小T

不想費時間去檢驗另一批礦產,所以他想通過調整引數W 的值,讓檢驗結果儘可能的靠近

標準值S,即使得S-Y 的絕對值最小。請你幫忙求出這個最小值。

輸入輸出格式

輸入格式:

輸入檔案qc.in 。

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

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

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

輸出格式:

輸出檔名為qc.out。

輸出只有一行,包含一個整數,表示所求的最小值。

輸入輸出樣例

輸入樣例#1:

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

輸出樣例#1:

10

說明

【輸入輸出樣例說明】

當W 選4 的時候,三個區間上檢驗值分別為 20、5 、0 ,這批礦產的檢驗結果為 25,此

時與標準值S 相差最小為10。

【資料範圍】

對於10% 的資料,有 1 ≤n ,m≤10;

對於30% 的資料,有 1 ≤n ,m≤500 ;

對於50% 的資料,有 1 ≤n ,m≤5,000;

對於70% 的資料,有 1 ≤n ,m≤10,000 ;

對於100%的資料,有 1 ≤n ,m≤200,000,0 < wi, vi≤10^6,0 < S≤10^12,1 ≤Li ≤Ri ≤n 。

——————————————————————————————————————————-

注意

看資料範圍,有些變數要開long long

思路1

這是一個水思路:
在礦石重量Wi中找到一個最大值和一個最小值,作為二分的起點和終點,然後進行二分,因為這道題終答案具有遞增的性質,所以當算出來的值大於標準時,r = mid,反之l = mid。然後寫一個函式,用來計算每一次的答案,不斷更新

bool check(ll x) {
    ll sum = 0;
    ll he = 0;
    ll ans = 0;
    for(ll i = 1; i <= m; ++i) {
        sum = 0, he = 0;
        for(ll j = b[i].L; j <= b[i].R; ++j) {
            if(a[j].w >= x) {
                sum ++;
                he += a[j].v; 
            }           
        }
        ans += sum * he;
    }

    if(abs(ans - S) < ANS){
        ANS = abs(ans - S);
    }
    if(ans > S) return true;
    else return false;      
}
-------------------------------

ll l = v[1], r = v[n];
    ll mid;

    while(l + 1 < r) {
        mid = (l + r) / 2;
        if(check(mid)){
            l = mid; 
        }
        else r = mid;
    }

思路二

二分的基本思路不變,只是在計算的時候每次預處理一個字首和,這樣可以僅剩不少時間

for(int i=1;i<=n;++i){
        if(W[i]>=w) v[i]=v[i-1]+V[i],num[i]=num[i-1]+1;
       else v[i]=v[i-1],num[i]=num[i-1];    
    }
    for(int i=1;i<=m;++i){
        Y+=(v[R[i]]-v[L[i]-1])*(num[R[i]]-num[L[i]-1]); 
    }

最終程式碼

#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 9;
long long n,m;
long long L[N],R[N];
long long W[N],V[N],num[N];
long long minx=1e7+1,maxx=-1;
long long ans=1e13+1,s;
long long  Y=0;
long long  v[N];
inline long long check(long long w){
    Y=0;
    memset(v,0,sizeof(v));
    memset(num,0,sizeof(num));
    for(int i=1;i<=n;++i){
        if(W[i]>=w) v[i]=v[i-1]+V[i],num[i]=num[i-1]+1;
       else v[i]=v[i-1],num[i]=num[i-1];    
    }
    for(int i=1;i<=m;++i){
        Y+=(v[R[i]]-v[L[i]-1])*(num[R[i]]-num[L[i]-1]); 
    }
    return Y ;
}
int main (){
    scanf("%lld%lld%lld",&n,&m,&s);
    for(int i=1;i<=n;++i){
        scanf("%lld %lld",&W[i],&V[i]);
        minx=min(W[i],minx);
        maxx=max(W[i],maxx);
    }
    for(int i=1;i<=m;++i){
       scanf("%lld %lld",&L[i],&R[i]);
    }
    long long l=minx,r=maxx+1;
    while(l+1<r){
        long long mid=l+r>>1;
        ans=min(ans,llabs(check(mid)-s));
        if(Y>s) l=mid;
        else r=mid;
    }
    long long ls=min(llabs(check(l)-s),llabs(check(r)-s));
    printf("%lld\n",ls>ans?ans:ls);
}