洛谷题解:P1314 [NOIP2011 提高组] 聪明的质监员

洛谷题解:P1314 [NOIP2011 提高组] 聪明的质监员

题目:

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

1 、给定m个区间 [li,ri]

2 、选出一个参数 W;

3 、对于一个区间 [li,ri],计算矿石在这个区间上的检验值 yi :

其中 j 为矿石编号。

这批矿产的检验结果 y 为各个区间的检验值之和。即:

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

输入格式:

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

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

扫描二维码关注公众号,回复: 12916360 查看本文章

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

输出格式:

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

样例:

输入:

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

输出:

10

分析:

可以看出来,这个函数是随W的增加递减的,由于要对W进行查找遍历,所以使用二分,找到一个结果最接近s的W。两个端点的值自然是w数组的最小值与最大值。

然后check()的就是公式结果的值与s的大小关系,如果ans > s,说明ans还需要再小,函数还是单减的,所以要选择W更大的区间。

然后对于这个函数值的计算,对于每一个W,都要循环m次,每一次循环内还要从l[i]r[i]再循环进行判断,这样执行了TLE了。看来是需要用前缀和,这样就免去了从l[i]r[i]的循环的复杂度,但是需要提前对数组单另判断一次,不符合要求的不自增。

代码如下:

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>

using namespace std;

long long n, m, s;
long long w[300005] = {
    
    0};
long long v[300005] = {
    
    0};
long long l[300005] = {
    
    0};
long long r[300005] = {
    
    0};
long long pre_w[300005] = {
    
    0};
long long pre_v[300005] = {
    
    0};
long long mini = 2147483647;
long long maxi = -1;
long long result = 0;

long long C_Y(long long W)
{
    
    
    long long ans = 0;
    memset(pre_v,0,sizeof(pre_v));
    memset(pre_w,0,sizeof(pre_w));
    for(long long i = 1;i <= n;i++)
    {
    
    
        if(w[i] >= W)
        {
    
    
            pre_w[i] = pre_w[i - 1] + 1;
            pre_v[i] = pre_v[i - 1] + v[i];
        }
        else
        {
    
    
            pre_w[i] = pre_w[i - 1];
            pre_v[i] = pre_v[i - 1];
        }
    }
    for(long long i = 1;i <= m;i++)
    {
    
    
        ans += (pre_w[r[i]] - pre_w[l[i] - 1]) * (pre_v[r[i]] - pre_v[l[i] - 1]);
    }
    return ans;
}

bool check(long long ans)
{
    
    
    result = llabs(ans - s);
    if(ans > s)
    {
    
    
        return true;
    }
    else
    {
    
    
        return false;
    }
}

int main()
{
    
    
    cin>>n>>m>>s;
    for(long long i = 1;i <= n;i++)
    {
    
    
        cin>>w[i]>>v[i];
        maxi = max(maxi, w[i]);
        mini = min(mini, w[i]);
    }
    for(long long j = 1;j <= m;j++)
    {
    
    
        cin>>l[j]>>r[j];
    }
    long long left = mini, right = maxi;
    long long mid;
    long long ans = 0x3f3f3f3f3f3f3f3f;
    while(left <= right)
    {
    
    
        mid = (right + left) / 2;
        if(check(C_Y(mid)))
        {
    
    
            left = mid + 1;
        }
        else
        {
    
    
            right = mid - 1;
        }
        if(ans > result)
        {
    
    
            ans = result;
        }
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_46052886/article/details/113573299
今日推荐