题目描述
小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 \(n\) 个矿石,从 \(1\) 到 \(n\) 逐一编号,每个矿石都有自己的重量 \(w_i\) 以及价值 \(v_i\) 。检验矿产的流程是:
1 、给定 \(m\) 个区间 \([L_i,R_i]\) ;
2 、选出一个参数 \(W\);
3 、对于一个区间 \([L_i,R_i]\),计算矿石在这个区间上的检验值 \(Y_i\) :
这批矿产的检验结果 \(Y\) 为各个区间的检验值之和。即: \(Y_1+Y_2...+Y_m\)
若这批矿产的检验结果与所给标准值 \(S\) 相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数 \(W\) 的值,让检验结果尽可能的靠近标准值 \(S\) ,即使得 \(S−Y\) 的绝对值最小。请你帮忙求出这个最小值。
输入输出格式
输入格式:
第一行包含三个整数 \(n,m,S\) ,分别表示矿石的个数、区间的个数和标准值。
接下来的 \(n\) 行,每行 \(2\) 个整数,中间用空格隔开,第 \(i+1\) 行表示 \(i\) 号矿石的重量 \(w_i\) 和价值 \(v_i\) 。
接下来的 \(m\) 行,表示区间,每行 \(2\) 个整数,中间用空格隔开,第 \(i+n+1\) 行表示区间 \([L_i,R_i]\) 的两个端点 \(L_i\) 和 \(R_i\) 。注意:不同区间可能重合或相互重叠。
输出格式:
一个整数,表示所求的最小值。
输入输出样例
输入样例#1:
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
输出样例#1:
10
分析
方法:二分+前缀和思想
容易想到二分\(w\)的值,二分的边界一定是\(min{w_i}<=w<=max{w_i}(i\in[1,n])\)。
那么\(check\)函数怎么写呢?
首先,\(w\)越大,能满足条件的矿产就越少,即\(y\)越小,当\(y<s\)时,则可以将\(w\)取小;\(w\)越小,相应地,\(y\)也就越小,当\(y>s\)时,则可以将\(w\)取大。当然,如果\(y=s\)时,可以直接输出答案了。于是,我们就这样让\(w\)不断的向\(s\)逼近,直到不能再近为止。
复杂度:\(O((n+m)log(maxw_i-minw_i))\)
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define il inline
#define re register
#define maxn 200007
#define mymax(a,b) a>b?a:b
#define mymin(a,b) a<b?a:b
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x){
T f=1;x=0;char c;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=x*10+(c^48);
x*=f;
}
struct stone{
int w,v;
}sto[maxn];
struct judge{
int l,r;
}jud[maxn];
int n,m,mn,mx;
ll s,y,ret,ans;
ll sum[maxn],cnt[maxn];
bool check(int k){
y=0;
memset(sum,0,sizeof(sum));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;++i){
if(sto[i].w>=k){
sum[i]=sum[i-1]+sto[i].v;
cnt[i]=cnt[i-1]+1;
}
else{
sum[i]=sum[i-1];
cnt[i]=cnt[i-1];
}
}
for(int i=1;i<=m;++i) y+=(sum[jud[i].r]-sum[jud[i].l-1])*(cnt[jud[i].r]-cnt[jud[i].l-1]);
ret=llabs(s-y);
return y>s;
}
int main(){
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
mn=2147486217;ans=0x3f3f3f3f3f3f3f;
read(n),read(m),read(s);
for(int i=1;i<=n;++i) read(sto[i].w),read(sto[i].v),mx=max(mx,sto[i].w),mn=min(mn,sto[i].w);
for(int i=1;i<=m;++i) read(jud[i].l),read(jud[i].r);
int l=mn-1,r=mx+2;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))
l=mid+1;
else
r=mid-1;
ans= ret<ans ? ret : ans;
}
printf("%lld",ans);
return 0;
}