[NOIP提高组]2011年 Day2-T2 聪明的质监员

本文介绍了一种通过二分搜索和前缀和方法优化矿石价值的算法。该算法首先枚举W值,利用二分搜索找到使S-Y绝对值最小的W值,再使用前缀和方法快速计算区间内符合条件的矿石价值总和,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题目大意

给出n个矿石,每个矿石都有自己的重量wi 以及价值vi
1、给定m 个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi :这个区间上所有重量大于等于W的矿石数目与它们的价值和的乘积。这批矿产的检验结果Y 为各个区间的检验值之和。
求出S-Y的绝对值最小

题目解析

第一:二分

一看题目,很明显是要枚举W值,取求Y值,并找出S-Y的绝对值的最小值

所以利用二分枚举W的值

因为选择的矿石的重量大于等于W
所以W越大,所选的矿石就越少,Y值越小;W越小,所选的矿石越多,Y值越大

所以Y随X的增大而减小

由此推出二分的条件:
当Y>S 时,需要增大W来减小Y,从而|Y-s|变小;
当Y=s时,|Y-s|==0;
当Y<s时,需要减小W来增大Y,从而|Y-s|变大;

第二:前缀和

我们在计算区间的和时,通常是用前缀和的方法来缩减时间,直接模拟是n2的,而前缀和成了2n

矿石价值和以及矿石数量和

所有区间满足条件的 (矿石数量 * 矿石价值)的和

求最小值

代码

#include<bits/stdc++.h>
#define N 200005
using namespace std;
long long n,m,S,W,sum,ans;
long long w[N],v[N],l[N],r[N],a[N],b[N];
bool check()
{
	sum=0;
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	//a表示满足条件的矿石数量的前缀和,b表示满足条件的矿石价值的前缀和 
	for(int i=1;i<=n;i++)
	 if(W<=w[i])//满足条件重量大于等于W 
	 {
	   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++)
	 sum+=(a[r[i]]-a[l[i]-1])*(b[r[i]]-b[l[i]-1]);
	if(sum>S) return true;
	return false;
}
int main()
{
	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 left=1,right=1e6,mid;
	while(left<=right)//二分 
	{
	  mid=(left+right)>>1;
	  W=mid;
	  if(check()) left=mid+1;
	  else right=mid-1;
	  if(ans==0&&mid==500000)//随便写的,应付一下刚开始ans=0的情况 
	   ans=abs(sum-S);
	  ans=min(ans,abs(sum-S));//更新值 
	}
	cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值