聪明的质监员(二分答案,前缀和)

Posted ztz11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聪明的质监员(二分答案,前缀和)相关的知识,希望对你有一定的参考价值。

题目描述

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 nnn 个矿石,从 111 到 nnn 逐一编号,每个矿石都有自己的重量 wiw_iwi? 以及价值 viv_ivi? 。检验矿产的流程是:

1 、给定 m 个区间 [Li,Ri]

2 、选出一个参数 W

3 、对于一个区间 [Li,Ri],计算矿石在这个区间上的检验值 Yi

技术分享图片

这批矿产的检验结果 Y 为各个区间的检验值之和。即: Y1+Y2...+Ym?

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

输入输出格式

输入格式:

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

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

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

输出格式:

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

 思路:

暴力是不行的啦,而我又不会别的qwq

所以我就想办法开始优化

一看w才1e6,好办,可以二分答案了。

每次二分一个答案,然后验证

如果二分出来的答案s-y小于0,那么可能最优解在他的右边

反之亦然

记得每次都要更新答案哟。

怎么验证呢?

我们就要用到前缀和

每次二分出一个数,我就按这个条件O(N)算出前缀和

询问的回答就可以O(1)了

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rii register int i
#define rij register int j
#define inf 19260817
#define int long long 
using namespace std;
long long qzhv[1000005],qzhw[1000005],s,n,m,v[200005],w[200005];
long long maxn,minx;
long long z[200005],y[200005];
long long find(int wz)
{
    long long ans=0;
    memset(qzhv,0,sizeof(qzhv));
    memset(qzhw,0,sizeof(qzhw));
    for(rii=1;i<=n;i++)
    {
        qzhv[i]=qzhv[i-1];
        qzhw[i]=qzhw[i-1];
        if(w[i]>=wz)
        {
            qzhw[i]++;
            qzhv[i]+=v[i];
        }
    }
    for(rii=1;i<=m;i++)
    {
        long long ltt=z[i];
        long long kkk=y[i];
        ans+=(qzhw[kkk]-qzhw[ltt-1])*(qzhv[kkk]-qzhv[ltt-1]);
    }
    return ans;
}
long long qabs(long long val)
{
    if(val>0)
    {
        return val;
    }
    else
    {
        return val*(-1);
    }
}
signed main()
{
    maxn=-inf;
    minx=inf;
    scanf("%d%d%lld",&n,&m,&s);
    for(rii=1;i<=n;i++)
    {
        scanf("%d%d",&w[i],&v[i]);
        maxn=max(maxn,w[i]);
        minx=min(minx,w[i]); 
    }
    for(rii=1;i<=m;i++)
    {
        scanf("%d%d",&z[i],&y[i]);
    }
    int l=minx-1;
    int r=maxn+2;
    minx=9999999999999999;
    while(l!=r)
    {
        if(r-l==1)
        {
            minx=min(minx,min(qabs(find(l)-s),qabs(find(r)-s)));
            break;
        }
        int mid=(l+r)/2;
        long long qaq=find(mid);
        minx=min(minx,qabs(qaq-s));
        qaq=qaq-s;
        if(qaq<0)
        {
            r=mid;
        }
        else
        {
            l=mid;
        }
    }
    cout<<minx;
}

 

以上是关于聪明的质监员(二分答案,前缀和)的主要内容,如果未能解决你的问题,请参考以下文章

P1314 [NOIP2011 提高组] 聪明的质监员(二分&前缀和)

聪明的质监员 2011年NOIP全国联赛提高组(二分+前缀和)

$Noip2011/Luogu1314$ 聪明的质监员 二分+巧妙前缀和

[NOIP2011]聪明的质监员 题解

二分查找前缀和(洛谷1314聪明的质监员NOIP2011提高组)

luogu_P1314 聪明的质监员