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

一开始的想法是二分\(|s-y|\)的值t,然后根据二分到的值,看是否能够找到一个W,使\(s-t\le y\le s + t\),并且答案具有二段性,因为如果对于t能够找到一个W使得满足条件,那么所有比t大的值,都能够满足存在W使得\(s-t\le y\le s + t\),所以满足二段性,由于是整数二分,一定能够找到一个最小的t使得\(|s - y|=t\),但是这样的话,没想到怎么去求W...

思路:二分W,如果W的值使得s - y > 0,为了让|s - y|更小,应该让W小一些,如果W的值使得s - y <= 0,为了让|s - y|更小,应该让W大一些,在二分过程中求出\(|s-y|\)最小值(和之前做的二分有些区别,不是直接二分答案),另外,因为有区间求和,所以再算s - y的时候要用前缀和优化一下

#include<iostream>
#include<cstring>
using namespace std;

#define LL long long

const int N = 200010;

int n, m;
LL s, ans = 1e12 + 10;
int w[N], v[N], l[N], r[N];
LL a[N], b[N];

LL check(LL W){
    memset(a, 0, sizeof a);
    memset(b, 0, sizeof b);
    
    for(int i = 1; i <= n; i ++){
        if(w[i] >= W) a[i] += a[i - 1] + 1, b[i] += b[i - 1] + v[i];
        else a[i] = a[i - 1], b[i] = b[i - 1];
    }
    LL res = 0;
    for(int i = 1; i <= m; i ++)
        res += (a[r[i]] - a[l[i] - 1]) * (b[r[i]] - b[l[i] - 1]);
        
    return s - res;
}   

int main(){
    cin >> n >> m >> s;
    for(int i = 1; i <= n; i ++) cin >> w[i] >> v[i];
    for(int i = 1; i <= m; i ++) cin >> l[i] >> r[i];
    LL ll = 0, rr = 1e6;
    while(ll < rr){
        LL mid = ll + rr >> 1;
        LL val = check(mid);
        if(val > 0) rr = mid;
        else ll = mid + 1;
        ans = min(ans, abs(val));
    }
    
    cout << ans << endl;
    
    return 0;
}
posted @ 2021-09-13 20:45  yys_c  阅读(70)  评论(0编辑  收藏  举报