1. 程式人生 > >[NOIP2011] 聰明的質監員 二分+字首和

[NOIP2011] 聰明的質監員 二分+字首和

考試的時候打的二分但沒有用字首和維護。但是有個小細節手誤打錯了結果掛掉了。

絕對值的話可能會想到三分,但是注意到w增大的時候y是減小的,所以單調性很明顯,用二分就可以。但注意一個問題,就是二分最後的結果不一定是最優的,只是在它屬於的符號裡是最優的,所以需要最後存正負的最優解去比較。

至於check(),先把所有滿足wi>=W的所有條件的num(個數)和v(權值)在本位置加上,求字首和。

即∑vi(wi>=W);∑num(wi>=W)。最後用區間的話用字首和相減維護即可。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define pos(i,a,b) for(long long i=(a);i<=(b);i++)
#define N 410000
long long n,m,s;
long long w[N],v[N];
struct haha{
    long long left,right;
}cun[N];
long long ma;
long long l,r,ans;
long long aabs(long long x){
    return x<0?(-x):x;
}
long long temp,ans1=0x7fffffffffffffffLL,ans2=0x7fffffffffffffffLL;
long long pren[N],prevv[N];
long long check(long long x){
    temp=0;
    pos(i,1,n){
        pren[i]=pren[i-1]+(w[i]>=x);
        if(w[i]>=x){
            prevv[i]=prevv[i-1]+v[i];
        }
        else{
            prevv[i]=prevv[i-1];
        }
    }
    pos(i,1,m){ 
        temp+=(pren[cun[i].right]-pren[cun[i].left-1])*(prevv[cun[i].right]-prevv[cun[i].left-1]);
    }
    if(temp-s>0){
        ans1=ans1>temp-s?temp-s:ans1;
        return 1;
    }
    if(temp-s==0){
        printf("0");
        return 2;
    }
    if(temp-s<0){
        ans2=ans2>s-temp?s-temp:ans2;
        return 0;
    }
}
int main(){
    //freopen("qc.in","r",stdin);
    //freopen("qc.out","w",stdout);
    scanf("%lld%lld%lld",&n,&m,&s);
    pos(i,1,n){
        scanf("%lld%lld",&w[i],&v[i]);
        ma=max(w[i],ma);
    }
    pos(i,1,m){
        scanf("%lld%lld",&cun[i].left,&cun[i].right);
    }
    l=0,r=ma;
    while(l<=r){
        long long mid=(l+r)>>1;
        //cout<<"mid="<<mid<<"  l="<<l<<"  r="<<r<<endl;system("pause");
        long long qian=check(mid);
        if(qian==1)
            l=mid+1;
        if(qian==2)
            return 0;
        if(qian==0)
            r=mid-1;
    }
    long long ans=ans1>ans2?ans2:ans1;
    cout<<ans;
    return 0;
}