二分答案 NOIP 2011 聪明的质监员

题意: n n 个矿石,每个矿石都有自己的重量 wi,以及价值 vi v i 。接下来会进行以下四个操作:
1.给定 m m 个区间[li,ri]
2.选出一个参数 W W
3.对于一个区间[li,ri],计算矿石在这个区间上的检验值 Yi=j1jvj  j[li,ri]   wj>=W Y i = ∑ j 1 ∗ ∑ j v j ,     j ∈ [ l i , r i ]     并 且   w j >= W j j 是矿石编号。
4.这批矿产的检验结果Y=imYi

给定一个 S S ,求|SY|的最小值。

我们发现, W W 的值越大,Y的值越小,所以当 Y>S Y > S 时,我们需要增大 W W 的值,反之亦然。

我们二分W的值,对于每个二分到的 W W 值,我们根据要求求出j1 jvj ∑ j v j ,然后枚举每一个区间,求出 Y Y 的值。最后根据Y S S 的值判断增大还是减小W即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll w[202000],v[202000],sum[202000],le,ri,summ,maxx;
ll n,m,s,l[202000],r[202000],cnt[202000],ans=1e+12;
bool check(ll mid)
{
    for(int i=1;i<=n;++i)
    {
        sum[i]=0;
        cnt[i]=0;
    }
    summ=0;
    for(ll i=1;i<=n;++i)
    {
        if(w[i]>=mid)
        {
            sum[i]=sum[i-1]+v[i];
            cnt[i]=cnt[i-1]+1;
        }
        else
        {
            sum[i]=sum[i-1];
            cnt[i]=cnt[i-1];
        }
    }
    for(ll i=1;i<=m;++i)
    {
        ll x=sum[r[i]]-sum[l[i]-1];
        ll y=cnt[r[i]]-cnt[l[i]-1];
        summ+=x*y;
    }
    if(s<summ)
        return true;
    return false;
}
int main()
{
    cin>>n>>m>>s;
    for(ll i=1;i<=n;++i)
    {
        scanf("%lld%lld",&w[i],&v[i]);
        maxx=max(maxx,w[i]);
    }    
    le=0,ri=maxx;
    for(ll i=1;i<=m;++i)
        scanf("%lld%lld",&l[i],&r[i]);
    ll mid;
    while(ri>=le)
    {
        mid=(le+ri)/2;
        if(check(mid))
            le=mid+1;
        else
            ri=mid-1;
        if(ans>llabs(s-summ))
            ans=llabs(s-summ);
    }
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值