洛谷 [NOIP2011 提高组] 聪明的质监员(前缀和,二分)
传送门
解题思路
总体思路:二分W,对于每个W求得一个y,根据y-s的正负调整l和r,并且每次更新ans。
如何求y?
可以扫一遍矿石,用a数组记录下符合条件的数量的前缀和,b数组记录下符合条件的v的求点缀和;再枚举每个区间加起来即可。
AC代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e5+5;
int n,m,w[maxn],l[maxn],r[maxn],a[maxn];
long long s,v[maxn],ans=1e15,b[maxn];
long long work(int x){
long long res=0;
for(int i=1;i<=n;i++){
if(w[i]>=x){
a[i]=a[i-1]+1;
b[i]=b[i-1]+v[i];
}else{
a[i]=a[i-1];
b[i]=b[i-1];
}
}
for(int i=1;i<=m;i++) res+=(a[r[i]]-a[l[i]-1])*(b[r[i]]-b[l[i]-1]);
return res;
}
int main()
{
ios::sync_with_stdio(false);
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];
int l=0,r=1e6+5;
while(l!=r){
int mid=(l+r)/2;
long long now=work(mid);
if(now>s) l=mid+1;
else r=mid;
ans=min(ans,abs(s-now));
if(now==s){
cout<<0;
return 0;
}
}
cout<<ans;
return 0;
}
//NOIP2011提高组Day2 t2