ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NC16597 [NOIP2011]聪明的质监员

2022-06-28 19:39:37  阅读:119  来源: 互联网

标签:10 NOIP2011 int NC16597 质监 mid 检验 ge vsum


NC16597 [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_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 \]

其中 \(j\) 为矿石编号。

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

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

输入格式

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

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

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

10

提示

【输入输出样例说明】

当 \(W\) 选 \(4\) 的时候,三个区间上检验值分别为 \(20,5 ,0\) ,这批矿产的检验结果为 \(25\),此时与标准值 \(S\) 相差最小为 \(10\)。

【数据范围】

对于 $10% $ 的数据,有 \(1 ≤n ,m≤10\);

对于 $30% $的数据,有 \(1 ≤n ,m≤500\) ;

对于 $50% $ 的数据,有 $ 1 ≤n ,m≤5,000$;

对于 \(70\%\) 的数据,有 \(1 ≤n ,m≤10,000\) ;

对于 \(100\%\) 的数据,有 $ 1 ≤n ,m≤200,000$,\(0 < w_i,v_i≤10^6\),\(0 < s≤10^{12}\),\(1 ≤l_i ≤r_i ≤n\) 。

思路

知识点:二分。

由 $$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$$ ,发现 \(y_i\) 随 \(W\) 增大而减小,所以 \(S-Y\) 随 \(W\) 增大而增大,而答案就在 \(S-Y\) 的零点附近,可以二分 \(W\) ,得到最优的 \(W\) 来求得最优的 \(S-Y\)。由于答案并非严格零点,因此最终需要对零点两侧都要计算一遍取最合适的值。

然后,预处理出 \([w_i \ge W]\) 的前缀和 \(cnt[i]\) 与 \([w_i \ge W]v_i\) 的前缀和 \(vsum[i]\) ,能优化复杂度。

于是有 \(y_i=(cnt[r_i] - cnt[l_i-1]) \times (vsum[r_i] - vsum[l_i-1])\) ,得到 \(Y\) 返回 \(S-Y\) 的正负情况。

时间复杂度 \(O(n+m)\)

空间复杂度 \(O(n+m)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int n, m;
ll s;
int w[200007], v[200007];
ll cnt[200007], vsum[200007];
ll ans = ~(1LL << 63);

struct area {
    int l, r;
}a[200007];

ll check(int mid) {
    for (int i = 1;i <= n;i++) {
        cnt[i] = cnt[i - 1] + (w[i] >= mid);
        vsum[i] = vsum[i - 1] + (w[i] >= mid) * v[i];
    }
    ll y = 0;
    for (int i = 1;i <= m;i++) {
        y += (cnt[a[i].r] - cnt[a[i].l - 1]) * (vsum[a[i].r] - vsum[a[i].l - 1]);
    }
    ans = min(ans, abs(s - y));///记录最小值,收敛点附近两个点的值刚好能被mid经过
    return s - y >= 0;
    /* check(mid)代表W=mid的s-y 是否>= 0
    若是,则W需要变小;若否,则W需要变大
    Y随W增大而减少, 但增量未知,而我们要求的是|s-y|而非W, 所以需要记录收敛点(变号点)左右两点的最小值 */
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    cin >> n >> m >> s;
    for (int i = 1;i <= n;i++) cin >> w[i] >> v[i];
    for (int i = 1;i <= m;i++) cin >> a[i].l >> a[i].r;

    int l = 1, r = 1e6;
    while (l <= r) { ///二分W,因为Y(W)单调减,找到s-y的零点附近即可
        int mid = l + r >> 1;
        if (check(mid)) r = mid - 1;
        else l = mid + 1;
    }
    cout << ans << '\n';
    return 0;
}

标签:10,NOIP2011,int,NC16597,质监,mid,检验,ge,vsum
来源: https://www.cnblogs.com/BlankYang/p/16420758.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有