[NOIP2011提高组]聪明的质监员

题目描述﹡﹡﹡

小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n 个矿石,从1
到n 逐一编号,每个矿石都有自己的重量wi 以及价值vi。检验矿产的流程是:
1、给定m 个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi :这个区间上所有重量大于等于W的矿石数目与它们的质量和的乘积。
这批矿产的检验结果Y 为各个区间的检验值之和。
若这批矿产的检验结果与所给标准值S 相差太多,就需要再去检验另一批矿产。小T
不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近
标准值S,即使得S-Y 的绝对值最小。请你帮忙求出这个最小值。

【输入输出样例说明】
当 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≤106,0 < S≤1012,1≤Li≤Ri≤n。

输入格式

第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。
接下来的 n 行,每行2 个整数,中间用空格隔开,第i+1 行表示i 号矿石的重量wi 和价
值vi 。
接下来的 m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,
Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。

输出格式

输出只有一行,包含一个整数,表示所求的最小值。

题目本身并不难,只是在3个小时内想出解法而且还要做其他的题,确实很困难。

本题要小心数据范围!

 1 #include<iostream>
 2 //#include<fstream> 
 3 using namespace std;
 4 //ifstream scanf("cin.in");
 5 //ofstream cout("cout.out"); 
 6 
 7 
 8 int a[200001],b[200001],x[200001],y[200001],m,n;long long z=0,s,sumv[200001]={0},sums[200001]={0};
 9 int mid;
10 bool p=1; 
11 
12 bool check(){
13      int i;long long total=0;
14      
15      for(i=1;i<=n;++i)
16      {
17       sumv[i]=sumv[i-1];
18       sums[i]=sums[i-1];                
19       if(a[i]>=mid) {sumv[i]+=b[i];sums[i]++;}                
20                       } 
21       
22      for(i=1;i<=m;i++)
23      total+=(sums[y[i]]-sums[x[i]-1])*(sumv[y[i]]-sumv[x[i]-1]); 
24      
25      if(p==1) {z=abs(total-s);p=0;if(total<s) return 1;return 0;}
26      if(abs(total-s)<z&&total<s) {z=abs(total-s);return 1;}
27      if(abs(total-s)<z&&total>s) {z=abs(total-s);return 0;}
28      if(total<s) return 1;
29      return 0;
30      }
31 
32 int main()
33 {
34     int i,j,k,left=0,right=0;
35     scanf("%d%d%I64d",&n,&m,&s); 
36   //  cin>>n>>m>>s;
37     for(i=1;i<=n;i++)
38     {scanf("%d%d",&a[i],&b[i]);if(a[i]>right) right=a[i];}//cin>>a[i]>>b[i];
39     
40     for(i=1;i<=m;i++)
41     scanf("%d%d",&x[i],&y[i]);//cin>>x[i]>>y[i];
42     
43     mid=(left+right)/2;
44     while(left<=right)
45     {
46       if(check())  
47       right=mid-1;
48       else left=mid+1;  
49       mid=(left+right)/2;         
50                      }
51     
52     cout<<z<<endl;     
53     return 0;
54     
55     }
原文地址:https://www.cnblogs.com/noip/p/2582057.html