Description
HYSBZ 开学了!今年HYSBZ 有n 个男生来上学,学号为1…n,每个学生都必须参加军训。在这种比较堕落的学校里,每个男生都会有Gi 个女朋友,而且每个人都会有一个欠扁值Hi。学校为了保证军训时教官不会因为学生们都是人生赢家或者是太欠扁而发生打架事故,所以要把学生们分班,并做出了如下要求:
1.分班必须按照学号顺序来,即不能在一个班上出现学号不连续的情况。
2.每个学生必须要被分到某个班上。
3.每个班的欠扁值定义为该班中欠扁值最高的那名同学的欠扁值。所有班的欠扁值之和不得超过Limit。
4.每个班的女友指数定义为该班中所有同学的女友数量之和。在满足条件1、2、3 的情况下,分班应使得女友指数最高的那个班的女友指数最小。
请你帮HYSBZ 的教务处完成分班工作,并输出女友指数最高的班级的女友指数。
输入数据保证题目有解。
Input
第一行仅2 个正整数n, Limit,分别为学生数量和欠扁值之和的上限。
接下来n 行每行2 个正整数Hi,Gi,分别为学号为i 的学生的欠扁值和女友数。
Output
仅1 个正整数,表示满足分班的条件下女友指数最高的班级的女友指数。
Sample Input
4 6
4 3
3 5
2 2
2 4
Sample Output
8
【样例解释】
分班按照(1,2),(3,4)进行,这时班级欠扁值之和为4+2=6<=Limit,而女友指数最高的班级为(1,2),为8。容易看出该分班方案可得到最佳答案。
Data Constraint
对于20%的数据:n,Limit<=100
对于40%的数据:n<=1000
对于100%的数据:1<=n,Gi<=20000,1<=Hi,Limit<=10^7
题解
吐槽一句,这题水法跑得比正解快(数据水)。
由于是两个最值,那么显然想到了二分答案。
怎么判断呢?
DP 大法好,设f[i]表示到了i,满足到i结尾区间的女友值的和<当前二分出的答案时最小的欠扁值和(即最后与limit比较的东西),最后判断,只要f[n] < limit那就满足。
那么方程显然:
当然要满足条件: Σk<=ik=jg[k]<=mid Σ k = j k <= i g [ k ] <= m i d
接着,水法就是用单调栈维护next[i]表示i前面第一个比h[i]大的h。然后DP时直接往前面跳就好了,由于这是水法,所以不详细讲。
正解用线段树维护f[j],max(h[j+1]..h[i]),直接求答案就好了。
code:
uses math;
var
n,limit,i,bz,top:longint;
l,r,mid:int64;
h,g,last,ff,sum:array[0..20000]of longint;
z:array[0..20000,1..2]of longint;
f:array[0..320000,1..5]of longint; //1:max(h);2:min(h);3:min(f);4:ans;5:lazy
procedure down(x:longint);
begin
if f[x,5]=0 then
exit;
f[x*2,5]:=max(f[x*2,5],f[x,5]);
f[x*2,1]:=max(f[x*2,1],f[x,5]);
f[x*2,2]:=f[x*2,1];
f[x*2,4]:=f[x*2,3]+f[x*2,1];
f[x*2+1,5]:=max(f[x*2+1,5],f[x,5]);
f[x*2+1,1]:=max(f[x*2+1,1],f[x,5]);
f[x*2+1,2]:=f[x*2+1,1];
f[x*2+1,4]:=f[x*2+1,3]+f[x*2+1,1];
f[x,5]:=0;
end;
procedure did(x:longint);
begin
f[x,1]:=max(f[x*2,1],f[x*2+1,1]);
f[x,2]:=min(f[x*2,2],f[x*2+1,2]);
f[x,3]:=min(f[x*2,3],f[x*2+1,3]);
f[x,4]:=min(f[x*2,4],f[x*2+1,4]);
end;
procedure insert(v,l,r,x,y:longint);
var
mid:longint;
begin
if l=r then
begin
f[v,1]:=h[l];
f[v,2]:=h[l];
f[v,3]:=y;
f[v,4]:=h[l]+y;
exit;
end;
down(v);
mid:=(l+r) div 2;
if x<=mid then
insert(v*2,l,mid,x,y)
else
insert(v*2+1,mid+1,r,x,y);
did(v);
end;
procedure dg(v,l,r,x:longint);
var
mid:longint;
begin
if f[v,1]<x then
begin
f[v,1]:=x;
f[v,2]:=x;
f[v,4]:=f[v,3]+x;
f[v,5]:=x;
exit;
end;
if l=r then
exit;
down(v);
mid:=(l+r) div 2;
if f[v*2,2]<x then
dg(v*2,l,mid,x);
if f[v*2+1,2]<x then
dg(v*2+1,mid+1,r,x);
did(v);
end;
procedure change(v,l,r,x,y,z:longint);
var
mid:longint;
begin
if (l=x)and(r=y) then
begin
dg(v,l,r,z);
exit;
end;
down(v);
mid:=(l+r) div 2;
if y<=mid then
change(v*2,l,mid,x,y,z)
else
if x>mid then
change(v*2+1,mid+1,r,x,y,z)
else
begin
change(v*2,l,mid,x,mid,z);
change(v*2+1,mid+1,r,mid+1,y,z);
end;
did(v);
end;
procedure find(v,l,r,x,y,z:longint);
var
mid:longint;
begin
if (l=x)and(r=y) then
begin
ff[z]:=min(ff[z],f[v,4]);
exit;
end;
down(v);
mid:=(l+r) div 2;
if y<=mid then
find(v*2,l,mid,x,y,z)
else
if x>mid then
find(v*2+1,mid+1,r,x,y,z)
else
begin
find(v*2,l,mid,x,mid,z);
find(v*2+1,mid+1,r,mid+1,y,z);
end;
end;
function pd(x:longint):boolean;
var
i,j:longint;
begin
fillchar(f,sizeof(f),0);
fillchar(ff,sizeof(ff),63);
j:=1;
ff[0]:=0;
for i:=1 to n do
begin
while sum[i]-sum[j-1]>x do
inc(j);
insert(1,1,n,i,ff[i-1]);
change(1,1,n,j,i,h[i]);
find(1,1,n,j,i,i);
end;
if ff[n]<=limit then
exit(true);
exit(false);
end;
begin
readln(n,limit);
for i:=1 to n do
begin
readln(h[i],g[i]);
sum[i]:=sum[i-1]+g[i];
end;
for i:=n downto 1 do
begin
while (h[i]>z[top,1])and(top>0) do
dec(top);
last[i]:=z[top,2];
inc(top);
z[top,1]:=h[i];
z[top,2]:=i;
end;
l:=0;
r:=400000000;
while l<r do
begin
mid:=(l+r) div 2;
if pd(mid) then
r:=mid
else
l:=mid+1;
end;
writeln(l);
end.
正解打得我好辛苦。