P1314 [NOIP2011 提高组] 聪明的质监员-二分+前缀和

[NOIP2011 提高组] 聪明的质监员

题目描述

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n n n 个矿石,从 1 1 1 n n n 逐一编号,每个矿石都有自己的重量 w i w_i wi 以及价值 v i v_i vi 。检验矿产的流程是:

1 、给定$ m$ 个区间 [ l i , r i ] [l_i,r_i] [li,ri]

2 、选出一个参数 W W W

3 、对于一个区间 [ l i , r i ] [l_i,r_i] [li,ri],计算矿石在这个区间上的检验值 y i y_i yi

y i = ∑ j = l i r i [ w j ≥ W ] × ∑ j = l i r i [ w j ≥ W ] v j y_i=\sum\limits_{j=l_i}^{r_i}[w_j \ge W] \times \sum\limits_{j=l_i}^{r_i}[w_j \ge W]v_j yi=j=liri[wjW]×j=liri[wjW]vj

其中 j j j 为矿石编号。

这批矿产的检验结果 y y y 为各个区间的检验值之和。即: ∑ i = 1 m y i \sum\limits_{i=1}^m y_i i=1myi

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

输入格式

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

接下来的 n n n 行,每行两个整数,中间用空格隔开,第 i + 1 i+1 i+1 行表示 i i i 号矿石的重量 w i w_i wi 和价值 v i v_i vi

接下来的 m m m 行,表示区间,每行两个整数,中间用空格隔开,第 i + n + 1 i+n+1 i+n+1 行表示区间 [ l i , r i ] [l_i,r_i] [li,ri] 的两个端点 l i l_i li r i r_i ri。注意:不同区间可能重合或相互重叠。

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

10
#include <bits/stdc++.h>
using namespace std;
#define de(x) cout<<x<<" ";
#define Pu puts("");
#define sf(x) scanf("%lld",&x);
typedef long long ll;
const int N=2e5+10,mod=100003;
struct E{
    ll w,u;
}s[N];
ll n,m,ans;
ll S;
ll zuo[N],you[N];
ll prew[N],preu[N];
ll fun(int t){
    ll res=0;
    //ll cnt,sz;
    //超时:直接模拟时间复杂度是n^2
    // for(int i=1;i<=m;i++){
    //     cnt=sz=0;
    //     for(int j=zuo[i];j<=you[i];j++){
    //         if(s[j].w>=t){
    //             cnt++;
    //             sz+=s[j].u;
    //         }
    //     }
    //     res+=sz*cnt;
    // }
    //使用前缀和:时间复杂度是2*n
    memset(prew,0,sizeof(prew));
    memset(preu,0,sizeof(preu));
    for(int i=1;i<=n;i++){
        if(s[i].w>=t){
            prew[i]=prew[i-1]+1;
            preu[i]=preu[i-1]+s[i].u;
        }else{
            prew[i]=prew[i-1];
            preu[i]=preu[i-1];
        }
    }
    for(int i=1;i<=m;i++){
        res+=(prew[you[i]]-prew[zuo[i]-1])*(preu[you[i]]-preu[zuo[i]-1]);
    }
    return res;
}
int main(){
    cin>>n>>m>>S;
    ll w_max=0;
    for(int i=1;i<=n;i++){
        sf(s[i].w)sf(s[i].u)
        if(w_max<s[i].w){
            w_max=s[i].w;
        }
    }
    for(int i=1;i<=m;i++){
        sf(zuo[i])sf(you[i])
    }
    ll l=1,r=w_max+1,mid;
    ll t;
    ans=0x3f3f3f3f3f3f3f3f;
    while(l<r){
        mid=(l+r)/2;
        t=fun(mid);
        
        if(t==S) l=mid+1;
        else if(t>S) l=mid+1;
        else r=mid; 

        if(abs(t-S)<ans) ans=abs(t-S);
        //这里需要记录,因为如果x合适,mid可能会最终会跳动到x+1,x-1(二者没有x优)
    }
    de(ans)
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值