关于这道题二分的操作

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

ZXZ695 @ 2018-10-20 20:10:20

我的二分的返回值:

    if(Y-s>=0)return 1;//Y要变小   w要增大
    else return 0;//tot<s    w要减小      让Y增大

二分:

        if(judge(mid))
        l=mid+1;
        else
        r=mid;

调了一下就A了,我不明白,L 和 R 的 改变为什么这么做?


by ☆Ork_Morry☆✔ @ 2018-11-11 21:06:21

#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int w[maxn],v[maxn],l[maxn],r[maxn];
long long pre_n[maxn],pre_v[maxn];
long long Y,s,sum;
int n,m,mx=-1,mn=2147483647;
bool check(int W)
{   
    Y=0,sum=0;
    memset(pre_n,0,sizeof(pre_n));
    memset(pre_v,0,sizeof(pre_v));
    for(int i=1;i<=n;i++)
    {
        if(w[i]>=W) pre_n[i]=pre_n[i-1]+1,pre_v[i]=pre_v[i-1]+v[i];
        else pre_n[i]=pre_n[i-1],pre_v[i]=pre_v[i-1];
    }
    for(int i=1;i<=m;i++)
        Y+=(pre_n[r[i]]-pre_n[l[i]-1])*(pre_v[r[i]]-pre_v[l[i]-1]);

    sum=llabs(Y-s);
    if(Y>s) return true;
    else return false;

}
int main(){
//  freopen("qc.in","r",stdin);
//  freopen("qc.out","w",stdout); 
    scanf("%d %d %lld",&n,&m,&s); 
    for(int i=1;i<=n;i++)
    {
        scanf(" %d %d",&w[i],&v[i]);
        mx=max(mx,w[i]);
        mn=min(mn,w[i]);    
    }
    for(int i=1;i<=m;i++)
        scanf(" %d %d",&l[i],&r[i]);
    int left=mn-1,right=mx+2,mid;  //这里有的人说要特判左右端点的check,但是其实你把left开成mn-1,right开成mx+2(注意取mx+1时即为W比所有都大,Y是0,这个情况要考虑,所以+2包含mx+1)就可以包含左右端点的check了,会简单点。
    long long ans=999999999999999;
    while(left<=right)
    {
        mid=(left+right)>>1;
        if(check(mid))  left=mid+1;
        else right=mid-1;
        if(sum<ans) ans=sum;
    }
    printf("%lld",ans);
    return 0;
} 

|