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

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

题意

题目描述

  • 给定\(n\)个物品,给定每个物品的 重量 \(w_i\) 和 价值 \(v_i\)
  • 给定一个标准值 \(s\) 以及一个参数 \(w\)
  • 质检员每次会抽取\(m\)个区间,每次的抽检结果为 \(y = \sum_{l_i}^{r_i} (w_i \ge w) · \sum_{l_i}^{r_i} v_i\)
  • 求出 \(\min{\mid{y - s}\mid}\)

数据范围

\(1 \le n,m \le 2^5\), \(0 < w_i, v_i \le 10^6\) \(0 < s \le 10^12\), \(1 \le l_i \le r_i \le n\)

SOLUTION

\(y > s\) 的时候, \(y - s > 0\)\(y < s\) 的时候, \(y - s < 0\) 我们想让 \(y\) 尽可能的逼近 \(s\) 对于参数\(w\),当\(w\)越大,\(y\)越小
因此本题具有单调性。 考虑二分\(w\) 使得 \(y\) 尽可能的逼近\(s\) 即可

AC_CODE_1

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

using namespace std;

const int N = 2e5 + 10;

int n, m, l = INF, r = -INF;
LL s; // 不开 long long 见祖宗 | =^_^= |
int L[N], R[N];
int w[N], v[N];
LL p1[N], p2[N];

LL chk(int x) {
    for(int i = 1; i <= n; i ++ ) {
        p1[i] = p1[i - 1]; p2[i] = p2[i - 1];
        if(w[i] >= x) {
            p1[i] += v[i];
            p2[i] ++;
        }
    }
    // printf("\n%d\n", x);
    LL ANS = 0;
    rep(i, 1, m) {
        // printf("> %d %d %lld %lld\n",L[i], R[i], (p1[R[i]] - p1[L[i] - 1]) , (p2[R[i]] - p2[L[i] - 1]));
        ANS += (p1[R[i]] - p1[L[i] - 1]) * (p2[R[i]] - p2[L[i] - 1]);
    }
    return ANS;

}

inline void solve() {
    
    read(n); read(m); read(s);
    rep(i, 1, n) {
        read(w[i]); read(v[i]);
        l = min(l, w[i]); r = max(r, w[i]);
    }
    rep(i, 1, m) {
        read(L[i]); read(R[i]);
    }      
    l --, r ++;
    LL ans = INFF;
    while(l <= r) { // 由于 l = mid + 1, r = mid - 1   因此while循环里面的条件应该是 l <= r 
        int mid = l + r >> 1; 
        LL res = chk(mid); 
        if(res >= s) l = mid + 1; // 如果结果 y > s 我们就让 w 大一点 这样 y就会小一点更逼近 s
        else r = mid - 1; //同理
        ans = min(ans, abs(res - s));
        // printf("> %d %lld\n", mid, res);
    }
    printf("%lld\n", ans);
}
signed main() 
{
    // freopen("in.txt", "r", stdin);
    solve();
    return 0;
}
 

AC_CODE_2

//倍增写法
#include <cstdio>
#include <iostream>

#define LL long long

const int N = 2e5 + 10;

int n, m, ans = 1; LL s;
int w[N], v[N], L[N], R[N]; LL s1[N], s2[N];

inline LL chk(int W) {
    for(int i = 1; i <= n; ++ i) {
        s1[i] = w[i] >= W ? s1[i - 1] + 1 : s1[i - 1];
        s2[i] = w[i] >= W ? s2[i - 1] + v[i] : s2[i - 1];
    }
    LL res = 0;
    for(int i = 1; i <= m; ++ i ) {
        res += (s1[R[i]] - s1[L[i] - 1]) * (s2[R[i]] - s2[L[i] - 1]);
    }
    return res;
}

int main() {
    scanf("%d%d%lld", &n, &m, &s);
    for(int i = 1; i <= n; i ++ ) scanf("%d%d", &w[i], &v[i]);
    for(int i = 1; i <= m; i ++ ) scanf("%d%d", &L[i], &R[i]);
    for(int i = 17; i >= 0; -- i) {
        ans += chk(ans + (1 << i)) >= s ? (1 << i) : 0;
    }
    printf("%lld\n", std::min(chk(ans) - s, s - chk(ans + 1)));

    return 0;
}
posted @ 2021-11-11 12:51  ccz9729  阅读(111)  评论(0编辑  收藏  举报