描述
小 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。注意:不同区间可能重合或相互重叠。
输出格式
输出只有一行,包含一个整数,表示所求的最小值。
限制
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.