[题解] 聪明的质监员

[题解] 聪明的质监员 二分+前缀和

题目链接
对于这种>=x求x的问题,显然一秒钟就能想到二分。
∑ j = l i r i [ w j ≥ W ] × ∑ j = l i r i [ w j ≥ W ] v j \sum_{j = l_i}^{r_i}\left [ w_j \ge W \right ] \times \sum_{j = l_i}^{r_i}\left [ w_j \ge W \right ]v_j j=liri[wjW]×j=liri[wjW]vj
对于这个诡异的式子,可以看出用前缀和优化。
对于每个w,维护第一个式子和第二个式子的前缀和,然后计算y的总和。
时间复杂度为 O ( n + m ) O\left(n+m\right) O(n+m)
然后只需要二分分别找一下使 y ( w ) ≥ s y(w) \ge s y(w)s和使 y ( w ) ≤ s y(w) \le s y(w)s的w就可以了。
总的时间复杂度是 O ( ( n + m ) log ⁡ ( max ⁡ w i ) ) O\left( (n+m)\log \left(\max w_i \right)\right) O((n+m)log(maxwi))
注意 \quad 每个二分都需要特判!

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#define ll long long
#define pii pair<int,int>
using namespace std;

const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 2e5 + 10;

int n,m;
int l[maxn], r[maxn];
ll w[maxn] ,v[maxn];
ll sum1[maxn],sum2[maxn];
ll s;

ll cal(int x){//计算x的总的y值
	ll ans = 0;
	memset(sum1,0,sizeof(sum1));
	memset(sum2,0,sizeof(sum2));
	for(int i = 1; i <= n; i++){//对于每个x,单独计算出两个前缀和
		sum1[i] = sum1[i-1];
		sum2[i] = sum2[i-1];
		if(w[i] >= x){
			sum1[i]++;
			sum2[i] += v[i];
		}
	}
	for(int i = 1; i <= m; i++) ans += (sum1[r[i]] - sum1[l[i]-1])*(sum2[r[i]] - sum2[l[i]-1]);//计算y的和
	return ans;
}

void solve(){
	scanf("%d%d%lld",&n,&m,&s);
	int maxw = 0;
	for(int i = 1; i <= n; i++){
		scanf("%lld%lld",&w[i],&v[i]);
		maxw = max(maxw,(int)w[i]);
	}
	for(int i = 1; i <= m; i++){
		scanf("%d%d",&l[i],&r[i]);
	}
	int l1 = 0, r1 = maxw;
	while(l1 < r1){//找使y>=s的最小数
		int mid = l1 + r1 + 1>> 1;
		if(cal(mid) >= s) l1 = mid;
		else r1 = mid - 1;
	}
	ll ans1 = cal(l1) - s;
	l1 = 0, r1 = maxw;
	while(l1 < r1){//找使y<=s的最大数
		int mid = l1 + r1 >> 1;
		if(cal(mid) <= s) r1 = mid;
		else l1 = mid + 1;
	}
	ll ans2 = s - cal(l1);
	//注意,可能ans1<0 或者 ans2 <0 (没有该区间),需要特判。
	if(ans1 >= 0 && ans2 >= 0)	printf("%lld\n",min(ans1,ans2));
	else printf("%lld\n", ans1 >= 0 ? ans1 : ans2);
}

int main()
{
	solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值