聪明的质监员

Description
小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n 个矿石,从1到n 逐一编号,每个矿石都有自己的重量wi 以及价值vi。检验矿产的流程是:
1、给定m 个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi :这个区间上所有重量大于等于W的矿石数目与它们的价值和的乘积。这批矿产的检验结果Y 为各个区间的检验值之和。若这批矿产的检验结果与所给标准值S 相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近标准值S,即使得S-Y 的绝对值最小。请你帮忙求出这个最小值。

Input
第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。
接下来的 n 行,每行2 个整数,中间用空格隔开,第i+1 行表示i 号矿石的重量wi 和价
值vi 。
接下来的 m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,
Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。

Output
输出只有一行,包含一个整数,表示所求的最小值。

Sample Input
5 3 15 1 5 2 5 3 5 4 5 5 5 1 5 2 4 3 3

Sample Output
10

Data Constraint

Hint

【输入输出样例说明】
当 W 选4 的时候,三个区间上检验值分别为20、5、0,这批矿产的检验结果为25,此时与标准值S 相差最小为10。

【数据范围】
对于 10%的数据,有1≤n,m≤10;
对于 30%的数据,有1≤n,m≤500;
对于 50%的数据,有1≤n,m≤5,000;
对于 70%的数据,有1≤n,m≤10,000;
对于 100%的数据,有1≤n,m≤200,000,0 < wi, vi≤106,0 < S≤1012,1≤Li≤Ri≤n。

.
.
.
.
.
.
分析
在这里插入图片描述

公式解读:>=w的个数*价值和

大概意思就是说给了几组区间,在区间中选出j个重量大于等于W的矿石
这一个区间的检验值就为j * (v1 + v2 + …… + vj)
总的检验值之和Y为所有区间检验值加起来,那么容易得出,W取越小,我们能选的矿石就越多
检验值也就越大。反之就越小。那我们的任务就变成了求一个W,使得Y最靠近S

如果用O(n)来求W的话,貌似是不合理的。我们又发现W跟随Y具有单调性,这就变成了类似在单调函数中
求值的问题,用二分就很好解决。对于每一次计算检验值,显然模拟时间复杂度将会很高
于是在检验一个W值时,先跑一遍前缀和,满足条件的值加上,不满足条件的不加上
之后求m个区间的检验值就很容易了

注意二分的范围还有long long

.
.
.
.
.
程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
long long n,m,w[200010],v[200010],maxx=-2147483647,minn=2147483647,ll[200010],rr[200010];
long long s,sum[200010],num[200010];

int main()
{
	scanf("%lld%lld%lld",&n,&m,&s);
	for (int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&w[i],&v[i]);
		maxx=max(maxx,w[i]);
		minn=min(minn,w[i]);
	}
	for (int i=1;i<=m;i++)
		scanf("%lld%lld",&ll[i],&rr[i]);
	long long l=minn,r=maxx;
	long long ans=0X7fffffffffffffff;
	while (l<=r)
	{
		long long mid=(long long)((long long)l+r)/2;
		memset(sum,0,sizeof(sum));
		memset(num,0,sizeof(num));
		for (int i=1;i<=n;i++)
			if (w[i]>=mid)
			{
				sum[i]=(long long)sum[i-1]+v[i];
				num[i]=num[i-1]+1;
			} else
			{
				sum[i]=sum[i-1];
				num[i]=num[i-1];
			}
		long long tj=0;
		for (int i=1;i<=m;i++)
			tj=(long long)tj+(long long)((long long)num[rr[i]]-num[ll[i]-1])*((long long)sum[rr[i]]-sum[ll[i]-1]);
		long long jg=abs((long long)s-tj);
		if (jg<ans) ans=jg;
		if (tj>=s) l=mid+1;
		if (tj<s) r=mid-1;
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/YYC-0304/p/10292782.html