题解 P1314 【聪明的质监员】

hicc0305

2018-06-19 18:26:49

Personal

### 【问题描述】 小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1 到 n 逐一编号,每个矿石都有自己的重量 wi 以及价值 vi。检验矿产的流程是: 1、给定 m 个区间[Li,Ri]; 2、选出一个参数 W; 3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值 Yi : 这批矿产的检验结果 Y 为各个区间的检验值之和。即: Y1+Y2...+Ym 若这批矿产的检验结果与所给标准值 S 相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近标准值 S ,即使得 S−Y 的绝对值最小。请你帮忙求出这个最小值。 ### 【输入数据】 第一行包含三个整数 n,m,Sn,m,S ,分别表示矿石的个数、区间的个数和标准值。 接下来的 nn 行,每行 22 个整数,中间用空格隔开,第 i+1i+1 行表示 ii 号矿石的重量 w_iw i ​ 和价值 v_iv i ​ 。 接下来的 m 行,表示区间,每行 2 个整数,中间用空格隔开,第 i+n+1 行表示区间 [Li,Ri]的两个端点 Li 和 Ri 。注意:不同区间可能重合或相互重叠。 ### 【输出数据】 一个整数,表示所求的最小值。 ### 【输入样例 1】 5 3 15 1 5 2 5 3 5 4 5 5 5 1 5 2 4 3 3 ### 【输出样例 1】 10 ### 【数据范围】 对于 10% 的数据,有 1 ≤n ,m≤101≤n,m≤10 ; 对于 30% 的数据,有 1 ≤n ,m≤5001≤n,m≤500 ; 对于 50% 的数据,有 1 ≤n ,m≤5,0001≤n,m≤5,000 ; 对于 70% 的数据,有 1 ≤n ,m≤10,0001≤n,m≤10,000 ; 对于 100% 的数据,有 1≤ n ,m ≤200,000,0< wi,vi ≤10^6,0< S ≤10^12,1 ≤Li ≤ Ri ≤ n,1 ≤n,m ≤200,000,0< wi,vi ≤10^6,0< S ≤10^12,1≤ Li ≤ Ri ≤n。 ------------ 只需要二分答案,找离s最近的答案即可,并不难 ```cpp #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define N 200001 #define M 200001 #define ll long long using namespace std; int n,m,l[M],r[M],v[N],w[N]; int maxw; ll ans,tmp,s; ll sumv[N],sumi[N]; ll judge(int W){ ll res = 0; sumv[0] = sumi[0] = 0ll; for(int i = 1;i <= n;i++){ if(w[i] >= W)sumv[i] = sumv[i - 1] + (ll)v[i],sumi[i] = sumi[i - 1] + 1ll; else sumv[i] = sumv[i - 1],sumi[i] = sumi[i - 1]; } for(int i = 1;i <= m;i++) res += (sumi[r[i]] - sumi[l[i] - 1]) * (sumv[r[i]] - sumv[l[i] - 1]); return res; } int main() { scanf("%d %d %lld",&n,&m,&s); for(int i = 1;i <= n;i++) scanf("%d %d",&w[i],&v[i]),maxw = max(maxw,w[i]); for(int i = 1;i <= m;i++) scanf("%d %d",&l[i],&r[i]); int L = 1,R = maxw; while(L <= R){ int mid = (L + R) / 2; tmp = judge(mid); if(tmp <= s)R = mid - 1; else L = mid + 1; if(abs(s - ans) > abs(tmp - s))ans = tmp; } printf("%lld",abs(ans - s)); return 0; } ```