CODE[VS] 1138 聪明的质检员 二分+前缀和

本文介绍了一种通过调整参数使矿产检验结果接近标准值的算法。该算法利用二分法和前缀和技巧处理大量区间求和问题,有效地减少了计算复杂度。

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

题目地址

题目描述 Description
小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n 个矿石,从1到n 逐一编号,每个矿石都有自己的重量wi 以及价值vi。检验矿产的流程是:见图
--------------------------------------------------------
若这批矿产的检验结果与所给标准值S 相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近标准值S,即使得S-Y 的绝对值最小。请你帮忙求出这个最小值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c4PnskVQ-1601000036809)(http://codevs.cn/media/image/problem/1138_1.png)]
--------------------------------------------------------
输入描述 Input Description
--------------------------------------------------------
第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。接下来的 n 行,每行2 个整数,中间用空格隔开,第i+1 行表示i 号矿石的重量wi 和价值vi 。 接下来的 m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。
输出描述 Output Description
------------------------------------------------------------------------
输出只有一行,包含一个整数,表示所求的最小值。
水题分析 Waterproblem Analysis
---------------------------------------------------------
也是夏令营二分的必讲题,因为是对于区间求和而且区间数目多不方便暴力枚举,就很显然是要求一个前缀和了,对于每一个二分值求一个前缀和即可。注意数据范围比较大,用long long类型就可以过了。这个题比较水,而且不是DP不用过多分析,就显得的整篇博文很短,其实没什么关系,只要贴一个代码就很长了。

附上代码:

#include<iostream>
#include<cmath>
#include<cstdio> 
#define ll long long
#define MAXN 200010
using namespace std;
ll n,m,s;
int w[MAXN],v[MAXN];
long long dv[MAXN],t[MAXN],ans=0;
long long anss=21474836400006;
struct re
{
	int l,r;
}q[MAXN]; 
inline ll get_num()
{
	ll num=0;
	char c;
	bool flag=1;
	while((c=getchar())=='\n'||c=='\r'||c==' ');
	if(c=='-')flag=0;
	else num=c-'0';
	while(isdigit(c=getchar()))
	num=num*10+c-'0';
	return num*(flag?1:-1);
}
bool cheak(ll mid)
{
	for(int i=1;i<=n;i++)
	{
		if(w[i]>=mid)
		{
			t[i]=t[i-1]+1;
			dv[i]=dv[i-1]+v[i];
		}
		else
		{
			t[i]=t[i-1];
			dv[i]=dv[i-1];
		}
	}
    ans=0;
	for(int i=1;i<=m;i++)
	{
		ans+=(dv[q[i].r]-dv[q[i].l-1])*(t[q[i].r]-t[q[i].l-1]);
	}
	ll x=abs(ans-s);
	anss=min(anss,x); 
	if(ans>=s)return 1;
	else return 0;
}
int main()
{
	n=get_num();m=get_num();s=get_num();
	for(int i=1;i<=n;i++)
	{
		w[i]=get_num();
		v[i]=get_num();
	}
	for(int i=1;i<=m;i++)
	{
		q[i].l=get_num();
		q[i].r=get_num();
	}
	ll l=1,r=2147483646000;//这里之前r过小也没有AC
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		if(cheak(mid))l=mid+1;
		else r=mid-1; 
	}
	cout<<anss;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值