[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 }