【NOIP2011】聪明的质检员

题目描述

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 \(n\) 个矿石,从 \(1\)\(n\) 逐一编号,每个矿石都有自己的重量 \(w_i\) 以及价值 \(v_i\)​ 。检验矿产的流程是:
1 、给定 \(m\) 个区间 \([L_i,R_i]\)

2 、选出一个参数 \(W\)

3 、对于一个区间 \([L_i,R_i]\),计算矿石在这个区间上的检验值 \(Y_i\)

这批矿产的检验结果 \(Y\) 为各个区间的检验值之和。即: \(Y_1+Y_2...+Y_m\)

若这批矿产的检验结果与所给标准值 \(S\) 相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数 \(W\) 的值,让检验结果尽可能的靠近标准值 \(S\) ,即使得 \(S−Y\) 的绝对值最小。请你帮忙求出这个最小值。

输入输出格式

输入格式:

第一行包含三个整数 \(n,m,S\) ,分别表示矿石的个数、区间的个数和标准值。

接下来的 \(n\) 行,每行 \(2\) 个整数,中间用空格隔开,第 \(i+1\) 行表示 \(i\) 号矿石的重量 \(w_i\) ​和价值 \(v_i\)

接下来的 \(m\) 行,表示区间,每行 \(2\) 个整数,中间用空格隔开,第 \(i+n+1\) 行表示区间 \([L_i,R_i]\) 的两个端点 \(L_i\)\(R_i\) ​。注意:不同区间可能重合或相互重叠。

输出格式:

一个整数,表示所求的最小值。

输入输出样例

输入样例#1:

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

输出样例#1:

10

分析

方法:二分+前缀和思想
容易想到二分\(w\)的值,二分的边界一定是\(min{w_i}<=w<=max{w_i}(i\in[1,n])\)
那么\(check\)函数怎么写呢?
首先,\(w\)越大,能满足条件的矿产就越少,即\(y\)越小,当\(y<s\)时,则可以将\(w\)取小;\(w\)越小,相应地,\(y\)也就越小,当\(y>s\)时,则可以将\(w\)取大。当然,如果\(y=s\)时,可以直接输出答案了。于是,我们就这样让\(w\)不断的向\(s\)逼近,直到不能再近为止。
复杂度:\(O((n+m)log(maxw_i-minw_i))\)

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define il inline
#define re register
#define maxn 200007
#define mymax(a,b) a>b?a:b
#define mymin(a,b) a<b?a:b
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;

template<typename T>inline void read(T &x){
    T f=1;x=0;char c;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=x*10+(c^48);
    x*=f;
}

struct stone{
    int w,v;
}sto[maxn];

struct judge{
    int l,r;
}jud[maxn];

int n,m,mn,mx;
ll s,y,ret,ans;
ll sum[maxn],cnt[maxn];

bool check(int k){
    y=0;
    memset(sum,0,sizeof(sum));
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;++i){
        if(sto[i].w>=k){
            sum[i]=sum[i-1]+sto[i].v;
            cnt[i]=cnt[i-1]+1;
        }
        else{
            sum[i]=sum[i-1];
            cnt[i]=cnt[i-1];
        }
    }
    for(int i=1;i<=m;++i) y+=(sum[jud[i].r]-sum[jud[i].l-1])*(cnt[jud[i].r]-cnt[jud[i].l-1]);
    ret=llabs(s-y);
    return y>s;
}

int main(){
//  freopen("data.in","r",stdin);
//  freopen("data.out","w",stdout);
    mn=2147486217;ans=0x3f3f3f3f3f3f3f;
    read(n),read(m),read(s);
    for(int i=1;i<=n;++i) read(sto[i].w),read(sto[i].v),mx=max(mx,sto[i].w),mn=min(mn,sto[i].w);
    for(int i=1;i<=m;++i) read(jud[i].l),read(jud[i].r);
    int l=mn-1,r=mx+2;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))
            l=mid+1;
        else
            r=mid-1;
        ans= ret<ans ? ret : ans;
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hlw1/p/11247356.html