P1314 聪明的质检员NOIP 2011T2

https://www.luogu.org/problem/show?pid=1314#sub
看到这个题,想到二分是不难的,但是如果就直接上二分,简单暴力地求和,就会超时。O(mnlogw)
观察题目,区间可能是重合的或者叠盖,就考虑到前缀和来优化,将m*n缩减到m+n。
最终复杂度为O((m+n)*logw).

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#define LL long long
using namespace std;
int n,m;
LL s;
int w[200005],v[200005];
int L[200005],R[200005];
LL h[200005],num[200005];
int l=10000000,r;
LL check(int x)
{
    LL y=0,t=0,yy=0;
    /*for(int i=1;i<=m;i++)//O(m*n*logw) TLE 
    {
        y=0,t=0;
        for(int j=L[i];j<=R[i];j++)
         if(w[j]>=x) y+=v[j],t++;
        yy+=t*y;
    }*/
    //memset(h,0,sizeof(h));
    //memset(num,0,sizeof(num));
    for(int i=1;i<=n;i++)//O((m+n)*log w) 
    {
        h[i]=h[i-1];num[i]=num[i-1];
        if(w[i]>=x) h[i]+=v[i],num[i]++;
    }    
    for(int i=1;i<=m;i++)
      yy+=(h[R[i]]-h[L[i]-1])*(num[R[i]]-num[L[i]-1]);
    return yy; 
}
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]),r=max(r,w[i]),l=min(l,w[i]); 
    for(int i=1;i<=m;i++)
     scanf("%d%d",&L[i],&R[i]);
    int mid;
    LL ans=200000000000;
    while(l<=r)
    {
       mid=(l+r)>>1;
       LL Y=check(mid);
       if(Y<s)
        r=mid-1;
       if(Y>s) 
        l=mid+1;
       if(Y==s) {printf("0
");return 0;}
       ans=min(ans,abs(s-Y));
    }
    printf("%lld
",ans);
    fclose(stdin);fclose(stdout);
    return 0;
}
/*
5 3 11
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3

6 
*/ 
/*
5 1 5
1 5
2 5
3 5
4 5
5 5
1 3

0*/
/*
5 2 11
1 3
2 3
3 3
4 3
5 2
1 5
2 3

1*/
原文地址:https://www.cnblogs.com/dfsac/p/7587907.html