洛谷 [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

posted @ 2021-07-13 18:03  尹昱钦  阅读(43)  评论(0编辑  收藏  举报