题解:
我们首先考虑到,对于一个W,因为区间是确定的,算出来的检验值Y,当W减小时,Y是不增的,因为对满足条件的矿石是不降的(wi>=W)。同理当W增大时,Y是不增的。这意味着Y是一个关于W的单调函数,于是我们就可以二分W值。若当前二分值为Wi,算出来的值为Yi,若S-Yi>0,我们就要设法减小Y值以更靠近S,于是我们就将左边界增大,若S-Yi<0,我们就要设法将Y增大,于是我们就需要将右边界缩小。若S-Yi=0,这是最理想的情况了,就可以直接退出二分。
大体确定了,还剩下一个问题对于一个W我们如何快速的求出Y。考虑到矿石的价值与数量(wi>=W)是满足“区间可减性(我自己造的词)”的,具体就是若设sum[i]表示从1~i中所有满足条件的矿石的价值和,那么区间[L,R]中满足条件的矿石和为sum[R]-sum[L-1],对于矿石出现的数量也是满足这个条件的。于是我们可以先O(n)扫一遍求出价值与数量的前缀和,然后在O(m)扫一遍所有区间将每个区间的Y值加起来就可以了。总的时间复杂度为O((m+n)log(maxwi))。
附上代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=200005; int n,m,l=1,r; ll S,ans=((ll)1<<61); ll sum[N],Fre[N]; struct point1{ int w,v; }mine[N]; struct point2{ int L,R; }Sec[N]; int read(){ int x=0; char ch=getchar(); while(ch<'0' || ch>'9') ch=getchar(); while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x; } int main(){ scanf("%d%d%lld",&n,&m,&S); for(int i=1;i<=n;++i){ mine[i].w=read(),mine[i].v=read(); r=max(mine[i].w,r); } for(int i=1;i<=m;++i) Sec[i].L=read(),Sec[i].R=read(); while(l<=r){ memset(sum,0,sizeof(sum)),memset(Fre,0,sizeof(Fre)); ll ret=0; int W=(l+r)>>1; for(int i=1;i<=n;++i){//求前缀和 if(mine[i].w>=W) sum[i]=sum[i-1]+mine[i].v,Fre[i]=Fre[i-1]+1; else sum[i]=sum[i-1],Fre[i]=Fre[i-1]; } for(int i=1;i<=m;++i) ret+=(sum[Sec[i].R]-sum[Sec[i].L-1])*(Fre[Sec[i].R]-Fre[Sec[i].L-1]);//求Y值 ll t=S-ret; if(t>0){ ans=min(ans,t); r=W-1; }else if(t<0){ ans=min(ans,-t); l=W+1; }else if(t==0){ ans=0; break; } } printf("%lld",ans); return 0; }