题目链接
题意
小T是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n个矿石,从1到 n 逐一编号,每个矿石都有自己的重量wi以及价值vi。检验矿产的流程是:
1、给定m个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值 Yi :
Yi=
∑
\sum
∑j1*
∑
\sum
∑jVj且wj≥W,j是矿石编号
且wj≥W,j是矿石编号
这批矿产的检验结果Y为各个区间的检验值之和。即:Y=
∑
i
=
1
m
\sum_{i=1}^m
∑i=1mYi
若这批矿产的检验结果与所给标准值S相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W的值,让检验结果尽可能的靠近标准值S,即使得S-Y的绝对值最小。请你帮忙求出这个最小值。
思路
- 利用二分求解答案,对端点中点进行判断,
- 判断一个区间大于w的数目和它们和的积,可以使用前缀和,时间复杂度O(n)
- 注意最后输出答案时对相邻位置的判断
参考代码
#include<bits/stdc++.h>
using namespace std;
long long weight[1200010],value[1200010],ll[1200010],rr[1200010],num[1200010],sum[1200010];
long long n,m,s;
long long judge(int w)
{
for(int i=1; i<=n; i++)
{
if(weight[i]>=w)
{
num[i]=1;
sum[i]=value[i];
}
else
{
num[i]=0;
sum[i]=0;
}
}
for(int i=2; i<=n; i++)
{
num[i]+=num[i-1];
sum[i]+=sum[i-1];
}
long long re=0;
for(int i=1;i<=m;i++)
{
re+=(num[rr[i]]-num[ll[i]-1])*(sum[rr[i]]-sum[ll[i]-1]);
}
return re;
}
int main()
{
cin>>n>>m>>s;
long long l=0,r=1e18,ans=1e9;
for(int i=1; i<=n; i++)
{
cin>>weight[i]>>value[i];
}
for(int i=1; i<=m; i++)
{
cin>>ll[i]>>rr[i];
}
while(l<=r)
{
long long mid=(l+r)/2;
long long tmp=judge(mid);
if(tmp>=s)
{
l=mid+1;
}
else
{
r=mid-1;
}
}
cout<<min(abs(judge(r)-s),abs(judge(r+1)-s))<<endl;
}