聪明的质检员
描述
小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n个矿石,从1到n逐一编号,每个矿石都有自己的重量wi以及价值vi。检验矿产的流程是:
1、给定m个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的 检验值$Y_i$:
[Y_i=(sum_j {1}) imes(sum_j v_j) ,j in [L_i,R_i] land : w_i geqslant W]其中 $j$ 为矿石编号
这批矿产的 检验结果Y 为各个区间的检验值之和 。即:
[Y = sum _{i=1} ^m Y_i]
若这批矿产的 检验结果 与所给标准值S相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W的值,让 检验结果 尽可能的靠近标准值S,即使得 S-Y 的绝对值最小。请你帮忙求出这个最小值。
格式
输入格式
第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。
接下来的n行,每行2个整数,中间用空格隔开,第i+1行表示i号矿石的重量wi和价值vi 。
接下来的m行,表示区间,每行2个整数,中间用空格隔开,第i+n+1行表示区间[Li,Ri]的两个端点Li和Ri。 注意:不同区间可能重合或相互重叠。
输出格式
输出只有一行,包含一个整数,表示所求的最小值。
样例1
样例输入1
5 3 15 1 5 2 5 3 5 4 5 5 5 1 5 2 4 3 3
样例输出1
10
限制
1s
提示
样例说明:当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 ≤ 10^6,0 < S ≤ 10^12,1 ≤ Li ≤ Ri ≤ n。
首先我们注意到题目中的 $W$ 值越大, $Y$ 一定会越小. 这满足单调性, 所以可以考虑二分 $W$ 使其尽可能接近 $Y$ (考试的时候有人查答案的时候在运算时直接作了差, 然后就得套绝对值符号, 然后就只能三分最后被卡了233333)
(还记得我第一次学会二分答案的奇技淫巧还是在写CodeForces的Rating计算器的时候w)
然后就是如何在已知 $W$ 的情况下快速求出 $Y$ .
隔壁机房由于日常援疆然后无脑上树成功无故给复杂度加了个 $log$ 2333333
其实我们可以 $O(n)$ 预处理出前缀和, 然后 $O(1)$ 计算每个区间的查询. 前缀和要预处理两个, 分别是前 $i$ 个矿石中满足 $w_i geq W$ 的矿石的价值和 $sum$ 以及前 $i$ 个矿石中满足 $w_i geq W$ 的矿石个数. 然后就可以 $O(1)$ 响应每个区间查询辣w
单次计算一个 $W$ 值所对应的 $Y$ 的时间复杂度 $O(n+m)$. 总时间复杂度 $O(log(w_{max}) imes (n+m))$
参考代码
1 #include <set> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 #include <iostream> 7 #include <algorithm> 8 9 const int MAXN=200010; 10 const long long INF=0x7FFFFFFFFFFFFFFFll; 11 12 int n; 13 int m; 14 long long S; 15 int L[MAXN]; 16 int R[MAXN]; 17 long long maxw; 18 long long w[MAXN]; 19 long long v[MAXN]; 20 long long ans=INF; 21 long long sum[MAXN]; 22 long long count[MAXN]; 23 24 long long Y(int); 25 void Initialize(); 26 27 int main(){ 28 Initialize(); 29 int l=0; 30 int r=maxw; 31 while(l<=r){ 32 int mid=(l+r)/2; 33 long long tmp=Y(mid); 34 ans=std::min(ans,std::abs(tmp-S)); 35 if(tmp<S) 36 r=mid-1; 37 else 38 l=mid+1; 39 } 40 printf("%lld ",ans); 41 return 0; 42 } 43 44 long long Y(int W){ 45 // fprintf(stderr, "W=%d ", W); 46 long long ret=0; 47 memset(sum,0,sizeof(sum)); 48 memset(count,0,sizeof(count)); 49 for(int i=1;i<=n;i++){ 50 sum[i]=sum[i-1]; 51 count[i]=count[i-1]; 52 if(w[i]>=W){ 53 sum[i]+=v[i]; 54 count[i]++; 55 } 56 // fprintf(stderr, "sum[%d]=%lld ,cnt=%lld ", i,sum[i],count[i]); 57 } 58 for(int i=0;i<m;i++){ 59 ret+=(sum[R[i]]-sum[L[i]-1])*(count[R[i]]-count[L[i]-1]); 60 } 61 // fprintf(stderr, "ret=%lld ", ret); 62 return ret; 63 } 64 65 void Initialize(){ 66 freopen("qc.in","r",stdin); 67 freopen("qc.out","w",stdout); 68 scanf("%d%d%lld",&n,&m,&S); 69 for(int i=1;i<=n;i++){ 70 scanf("%lld%lld",w+i,v+i); 71 maxw=std::max(maxw,w[i]); 72 } 73 for(int i=0;i<m;i++){ 74 scanf("%d%d",L+i,R+i); 75 } 76 }