NOIP2011 聪明的质检员 题解

来源:互联网 发布:天河工商局软件路 编辑:程序博客网 时间:2024/04/28 02:12

描述

小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n个矿石,从1到n逐一编号,每个矿石都有自己的重量wi以及价值vi。检验矿产的流程是:
1、给定m个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi
Y i =( j 1)( j v j ) ,  j[L i ,R i ]w j W 
j是矿石编号

这批矿产的检验结果Y 为各个区间的检验值之和。即:Y= i=1 m Y i  
若这批矿产的检验结果与所给标准值S相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W的值,让检验结果尽可能的靠近标准值S,即使得S-Y的绝对值最小。请你帮忙求出这个最小值。

格式

输入格式

第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。

接下来的n行,每行2个整数,中间用空格隔开,第i+1行表示i号矿石的重量wi和价值vi 。

接下来的m行,表示区间,每行2个整数,中间用空格隔开,第i+n+1行表示区间[Li,Ri]的两个端点Li和Ri。注意:不同区间可能重合或相互重叠。

输出格式

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

样例1

样例输入1[复制]

5 3 151 52 53 54 55 51 52 43 3

样例输出1[复制]

10

限制

1s

提示

样例说明:当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 ≤ 10^6,0 < S ≤ 10^12,1 ≤ Li ≤ Ri ≤ n。

思路分析:

数据极大,直接搜索肯定完蛋,所以想到二分;而且发现Y是关于W的减函数,所以检验值会随着参数的提高而减小,可以二分参数W来求得最接近S的检验值。sum[i]表示1到i间重量>=W的数量,ans[i]为质量和,这样我们就可以用O(n)的时间算出检验值=(sum[y]-sum[x-1])*(ans[y]-ans[x-1])

代码:(简单易懂就不注释了)

const maxn:qword=1 shl 60;
var
     max,n,m,i:longint;
     min,s:int64;
     w,v:array[0..200001] of longint;
     sum,ans,x,y:array[0..200001] of int64;
procedure devided(r,l:longint);
var
     mid:longint;
     a:integer;
     i:longint;
     op:int64;
begin
      mid:=(r+l) shr 1;
      sum[0]:=0;ans[0]:=0;
      for i:=1 to n do begin
          if w[i]>=mid then a:=1 else a:=0;
          sum[i]:=sum[i-1]+a;
          ans[i]:=ans[i-1]+a*v[i];
      end;
      op:=0;
      for i:=1 to m do begin
          if maxn-op<(sum[y[i]]-sum[x[i]-1])*(ans[y[i]]-ans[x[i]-1]) then
          begin
            op:=s*3;
              break;
          end;
          inc(op,(sum[y[i]]-sum[x[i]-1])*(ans[y[i]]-ans[x[i]-1]));
      end;
      if op>s then begin
          if abs(op-s)<min then min:=abs(op-s);
          if r=l then exit;
          devided(mid+1,l);
      end else begin
          if abs(op-s)<min then min:=abs(op-s);
          if r=l then exit;
          devided(r,mid);
      end;
end;
begin
      readln(n,m,s);
      for i:=1 to n do begin
          readln(w[i],v[i]);
          if w[i]>max then max:=w[i];
      end;
      min:=s*3;
      for i:=1 to m do readln(x[i],y[i]);
      devided(1,max+1);
      writeln(min);
end.

(以上代码只可通过百分之九十五 ,由于时间超限)

解二:(完美AC)

1:给ans赋值成1 shl 32 40分
2:用了while循环 65分
3: AC

代码:

program a_1;
var
 answer,s,tot1,tot2:int64;
 i,j,l,r,mid,k,n,m,t,max:longint;
 a,b,w,v,d:array[1..300000]of longint;
 e:array[1..300000]of int64;
 c:array[1..300000]of boolean;
begin
 answer:=1000000000000000;
 readln(n,m,s);
 for i:=1 to n do
 begin
 readln(w[i],v[i]);
 if v[i]>max then
 max:=v[i];
 end;
 for i:=1 to m do
 readln(a[i],b[i]);
 l:=1;
 r:=max;
 mid:=(l+r)shr 1;
 repeat
 tot2:=0;
 fillchar(c,sizeof(c),false);
 fillchar(d,sizeof(d),0);
 fillchar(e,sizeof(e),0);
 for i:=1 to n do
 if w[i]>=mid then c[i]:=true;
 for i:=1 to n do if c[i] then
 begin d[i]:=d[i-1]+1;e[i]:=e[i-1]+v[i];end
 else begin d[i]:=d[i-1];e[i]:=e[i-1];end;
 for i:=1 to m do
 tot2:=tot2+(d[b[i]]-d[a[i]-1])*(e[b[i]]-e[a[i]-1]);
 if abs(tot2-s)<answer then answer:=abs(tot2-s); if tot2<s then
 begin
 r:=mid-1;
 mid:=(l+r)shr 1;
 end;
 if tot2>s then
 begin l:=mid+1;
 mid:=(l+r)shr 1;
 end;
 if tot2=s then
 begin writeln(0);
 halt;
 end;
 until r<l;
 writeln(answer);
end.



0 0
原创粉丝点击