据说这是一道内部题,所以这里没有办法贴上题目和数据,那些做到这道题的自行来看就可以了orz
好,对于接近满满一张纸的题目描述来捋一下题目要求:
n张牌,每张牌都有一个收益v[i],连续取任意张牌,求出最大收益、次打收益……第m大收益,收益值为绝对值
下面开始说思路。
这道题看上去其实还是很容易上手的,
第一,一般都能看出来要用到一维前缀和,利用一维前缀和我们能直到任意[l,r]之间的收益和(并没有加绝对值)
第二,利用贪心,我们会发现由于我们的前缀和是可能带负数的,而我们需要的是最大到第m大收益,如何让收益最大呢?这答案就可能出现在一下两种情况:
(1)直接从第1张开始选,到第x张,即前缀和的绝对值
(2)不从第一张开始选,从任意[l,r]选取,要让收益最大到次打的话,
答案可能出现的范围一定是:前m大前缀和和前m小前缀和两两组合做差的结果
易知,我们需要对前缀和进行排序
=w=
分水岭来了,容易想到也容易做出来的是50分算法,即排序后,开一个数组存abs(sum[i]),然后枚举前k大前缀和和前k小前缀和做差,再排序取前m个答案输出,时间复杂度为O(nlogn+k^2),
根据数范围 50%的1<=k<=1000,,100% 1<=n<=100000,1<=k<=n,期望得分,50分
那么如何优化O(k^2)呢 0.0
假如我们取出的最大值是我们选的第i大的前缀和-第j小的前缀和,记做(i,j),那么下一个不从第一张取的答案范围一定是(i-1,j) 和 (i,j+1) 中的一个,所以我们发现我们每当取出最大值得时候,我们选择答案的范围就有所变化,我们需要选择一个高效的方法来维护这个变化
显然,我们要用最大堆=。=
那么我们没取出一个(i,j)就要加进去一个(i-1,j)和(i,j+1),很显然会有重复的在堆里捣乱,怎么避免重复呢?
我们先把abs(sum[i])都存到堆里后,我们选择第一大的前缀和即排序后的sum[n]与所有比它小的sum[i]做差,加入堆里,这样我们没取出一个(i,j) 只需要加入 一个(i-1,j) 就可以了(直接替换),保证了不重不漏=w=
取出m次堆首(最大堆),放到ansn数组里,再进行排序,然后从大到小输出即可=w=
注意:
1、前缀和sum数组中是没有绝对值的,而在选取最大值的时候,堆里要加入的是取完绝对值以后的
2、虽然我们每次取最大堆堆首但是那只是当前最大值,更新答案范围后可能会出现更大值,所以仍需要一遍排序
3、对于取出的数是(i,j)的我们要进行上述处理,那如果取出的数是前缀和的绝对值呢?显然我们只需要把这个数从堆里删去就可以了
4.在进行替换更新的时候,要注意同时更新(i,j)->(i-1,j)
5、所谓前m大的前缀和,就是排序后的sum[n-m+1]~sum[n],前m小的前缀和就是排序后的sum[1]~sum[m]
var
n,m,size,ans :longint;
i :longint;
a,sum,ansn :array[0..100010] of longint;
h,ll,rr :array[0..200010] of longint;
procedure swap(var a,b:longint);
var
c:longint;
begin
c:=a;a:=b;b:=c;
end;
procedure sort(l,r: longint);
var
i,j,x,y: longint;
begin
i:=l;
j:=r;
x:=sum[(l+r) div 2];
repeat
while sum[i]<x do inc(i);
while x<sum[j] do dec(j);
if not(i>j) then
begin
y:=sum[i];sum[i]:=sum[j];sum[j]:=y;
inc(i);j:=j-1;
end;
until i>j;
if l<j then sort(l,j);
if i<r then sort(i,r);
end;
procedure sort2(l,r: longint);
var
i,j,x,y: longint;
begin
i:=l;
j:=r;
x:=ansn[(l+r) div 2];
repeat
while ansn[i]<x do inc(i);
while x<ansn[j] do dec(j);
if not(i>j) then
begin
y:=ansn[i];ansn[i]:=ansn[j];ansn[j]:=y;
inc(i);j:=j-1;
end;
until i>j;
if l<j then sort(l,j);
if i<r then sort(i,r);
end;
//
procedure heapdown(i:longint);
var
t:longint;
begin
while (i*2<=size) do
begin
if (h[i*2]>h[i]) then t:=i*2 else t:=i;
if (i*2+1<=size) then
if (h[t]<h[i*2+1]) then t:=i*2+1;
if (t<>i) then
begin
swap(h[i],h[t]);
swap(ll[i],ll[t]);
swap(rr[i],rr[t]);
i:=t;
end else exit;
end;
end;
begin
assign(input,'zhugeliang.in');reset(input);
assign(output,'zhugeliang.out');rewrite(output);
read(n,m);
for i:=1 to n do read(a[i]);
for i:=1 to n do sum[i]:=sum[i-1]+a[i];
sort(1,n);
//
for i:=1 to n do h[i]:=abs(sum[i]);
for i:=n+1 to m+n do
begin
h[i]:=sum[n]-sum[i-n];
ll[i]:=i-n;
rr[i]:=n;
end;
size:=n+m;
for i:=size div 2 downto 1 do heapdown(i);
//
while (ans<m) do
begin
inc(ans);
ansn[ans]:=h[1];
if (ll[1]=0) and (rr[1]=0) then
begin
h[1]:=h[size];
rr[1]:=rr[size];
ll[1]:=ll[size];
dec(size);
heapdown(1);
end else
begin
h[1]:=sum[rr[1]-1]-sum[ll[1]];
rr[1]:=rr[1]-1;
heapdown(1);
end;
end;
sort2(1,m);
for i:=m downto 1 do writeln(ansn[i]);
close(input);close(output);
end.
——by Eirlys
转载请注明出处=w=
orz orz orz orz orz