Problem Description
小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n 个矿石,从1到n 逐一编号,每个矿石都有自己的重量wi 以及价值vi。检验矿产的流程是:
1、给定m 个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi :
这批矿的检验结果Y 为各个区间的检验值之和。即:
若这批矿产的检验结果与所给标准值S 相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近标准值S,即使得S-Y 的绝对值最小。请你帮忙求出这个最小值。
Input Format
第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。
接下来的 n 行,每行2 个整数,中间用空格隔开,第i+1 行表示i 号矿石的重量wi 和价值vi 。
接下来的 m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。
Output Format
输出只有一行,包含一个整数,表示所求的最小值。
Algorithm Design
二分答案+前缀和
Problem Analysis
求最接近标准的答案,很明显二分答案。当然二分Y明显很困难,应该分治W的值。这里又可以引入离散化思想,因为W一定是一个矿的质量,不然不会有变化。确定W之后就是怎么求Y的问题,首先要读懂题目,其实意思很简单,就是一个区间内可行矿的数量*可行矿的价值总和,因为区间老是重叠,如果循环处理,最大可能达到O(nm),所以想到前缀和处理,纪录1到i的可行矿数量和价值总和(一开始还想到线段树..),最后就是O((n+m)logn)的算法
Source Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 #define MAXN 200010 3 #define ll long long 4 5 using namespace std; 6 7 int n,m; 8 ll s,ans,sumx[MAXN]; 9 int w[MAXN],v[MAXN],b[MAXN],e[MAXN],sortw[MAXN],sum[MAXN]; 10 11 void search(int l,int r) 12 { 13 if(l==r-1) 14 return ; 15 int mid=(l+r)>>1; 16 ll y=0; 17 for(int i=1;i<=n;i++) 18 { 19 sum[i]=sum[i-1]; 20 sumx[i]=sumx[i-1]; 21 if(w[i]>=sortw[mid]) 22 sum[i]+=1,sumx[i]+=v[i]; 23 } 24 for(int i=1;i<=m;i++) 25 y+=(sum[e[i]]-sum[b[i]-1])*(sumx[e[i]]-sumx[b[i]-1]); 26 ans=min(ans,abs(y-s)); 27 if(y<s) 28 search(l,mid); 29 else 30 search(mid,r); 31 } 32 33 int main() 34 { 35 freopen("qc.in","r",stdin); 36 freopen("qc.ans","w",stdout); 37 cin>>n>>m>>s; 38 for(int i=1;i<=n;i++) 39 cin>>w[i]>>v[i],sortw[i]=w[i]; 40 for(int i=1;i<=m;i++) 41 cin>>b[i]>>e[i]; 42 sort(sortw+1,sortw+n+1); 43 ans=1000000000001; 44 search(0,n+1); 45 cout<<ans<<endl; 46 return 0; 47 }