聪明的质检员

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

 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 }
View Code
原文地址:https://www.cnblogs.com/qswx/p/9411310.html