NOIP2015普及组 Luogu 2672 CodeVS 5126 推销员-线段树-Pascal

题目描述 Description

阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第i家住户到入口的距离为Si米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。阿明每走1米就会积累1点疲劳值,向第i家住户推销产品会积累Ai点疲劳值。阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。

输入描述 Input Description

第一行有一个正整数N,表示螺丝街住户的数量。
接下来的一行有N个正整数,其中第i个整数Si表示第i家住户到入口的距离。数据保证S1≤S2≤…≤Sn<10^8。
接下来的一行有N个正整数,其中第i个整数Ai表示向第i户住户推销产品会积累的疲劳值。数据保证Ai<10^3。

输出描述 Output Description

输出N行,每行一个正整数,第i行整数表示当X=i时,阿明最多积累的疲劳值。

样例输入 Sample Input

【样例1】
5
1 2 3 4 5
1 2 3 4 5

【样例2】
5
1 2 2 4 5
5 4 3 4 1

样例输出 Sample Output

【样例1】
15
19
22
24
25

【样例2】
12
17
21
24
27

数据范围及提示 Data Size & Hint

1≤N≤100000
注:请用 scanf 输入。

 
【题解】
根据线段树思想,先建立两棵线段树,一棵存距离*2+疲劳值,也就是s[i]*2+a[i],一棵存疲劳值,即a[i]。
建树之后,先特判第一个,也就是第一棵树上最大的值直接输出来,将位置存在mx中。
mx的作用是存每次处理之后已经取过的最远的点,也就是i最大的点。
之后根据mx,在mx的左边寻找疲劳值最大的点,记为a1。在mx的右边寻找s[i]*2+a[i]最大的点,记为a2。
因为a1是在mx的左边,所以如果取左边直接ans加上a1(最大疲劳值)就行了。但是因为a2在mx右边,所以如果取右边除了加上a2还要减去s[mx]*2,因为我们等于加了双重的s,即ans原=s[mx]*2+a[mx] , a2=max(s[i]*2+a[i])(mx<i<=n) , ans现=ans原+a2-s[mx]*2=s[mx]*2+a[mx]+max(s[i]*2+a[i])-s[mx]*2=a[mx]+max(s[i]*2+a[i])。
那么如何判断左边还是右边更优呢?
根据我们已经知道a1与a2在左/右边的区别,那么直接判断加完之后的数哪个大就好啦,即ans+a1 ?>(<) ans+a2-s[mx]*2。
然后如果取右边的点,则要更新mx。
还有一个点,就是mx-1<0,mx+1>n,a1=0,a2=0这几种情况要特别注意,写的时候记得加个特判。
说道更新mx,就要记录最大点的所在位置,只要每棵树开一个p数组存位置就好啦。
然后取过的点记得清零就好了。
代码
uses math;
var n,i,j,ans,mx,a1,a2,pl1,pl2:longint;
var a,sum,mn,p,sum1,mn1,p1,v,aa:array[0..400020] of longint;
procedure up1(h:longint);
begin
  sum[h]:=sum[h<<1]+sum[h<<1 or 1];
  mn[h]:=max(mn[h<<1],mn[h<<1 or 1]);
  if mn[h<<1]>mn[h<<1 or 1] then p[h]:=p[h<<1] else p[h]:=p[h<<1 or 1];
end;
procedure up2(h:longint);
begin
  sum1[h]:=sum1[h<<1]+sum1[h<<1 or 1];
  mn1[h]:=max(mn1[h<<1],mn1[h<<1 or 1]);
  if mn1[h<<1]>mn1[h<<1 or 1] then p1[h]:=p1[h<<1] else p1[h]:=p1[h<<1 or 1];
end;
procedure build(h,l,r:longint);
var m:longint;
begin
  if l=r then begin sum[h]:=a[l];mn[h]:=a[l];p[h]:=l;
  sum1[h]:=aa[l];mn1[h]:=aa[l];p1[h]:=l;exit;end;
  m:=(l+r)>>1;build(h<<1,l,m);build(h<<1 or 1,m+1,r);up1(h);up2(h);
end;
procedure add1(h,c,l,r,x:longint);
var m:longint;
begin
  if (x<=l) and (x>=r) then begin
    inc(sum[h],c*(r-l+1));inc(mn[h],c);exit;end;
  m:=(l+r)>>1;
  if x<=m then add1(h<<1,c,l,m,x);
  if x>m then add1(h<<1 or 1,c,m+1,r,x);
  up1(h);
end;
procedure add2(h,c,l,r,x:longint);
var m:longint;
begin
  if (x<=l) and (x>=r) then begin
    inc(sum1[h],c*(r-l+1));inc(mn1[h],c);exit;end;
  m:=(l+r)>>1;
  if x<=m then add2(h<<1,c,l,m,x);
  if x>m then add2(h<<1 or 1,c,m+1,r,x);
  up2(h);
end;
function wm1(h,l,r,x,y:longint):longint;
var m,ans,num:longint;
begin
  if (x<=l) and (y>=r) then begin
  exit(mn[h]);end;
  m:=(l+r)>>1;ans:=-2147483647;
  if x<=m then
  begin
    num:=wm1(h<<1,l,m,x,y);
    if num>ans then begin ans:=num;pl1:=p[h<<1];end;
  end;
  if y>m then
  begin
    num:=wm1(h<<1 or 1,r+1,m,x,y);
    if num>ans then begin ans:=num;pl1:=p[h<<1 or 1];end;
  end;
  exit(ans);
end;
function wm2(h,l,r,x,y:longint):longint;
var m,ans,num:longint;
begin
  if (x<=l) and (y>=r) then begin
  exit(mn1[h]);end;
  m:=(l+r)>>1;ans:=-2147483647;
  if x<=m then
  begin
    num:=wm2(h<<1,l,m,x,y);
    if num>ans then begin ans:=num;pl2:=p1[h<<1];end;
  end;
  if y>m then
  begin
    num:=wm2(h<<1 or 1,r+1,m,x,y);
    if num>ans then begin ans:=num;pl2:=p1[h<<1 or 1];end;
  end; 
  exit(ans);
end;
begin
  read(n);for i:=1 to n do read(v[i]);
  for i:=1 to n do begin read(aa[i]);a[i]:=v[i]*2+aa[i];end;build(1,1,n);
  writeln(a[p[1]]);mx:=p[1];ans:=a[p[1]];
  add1(1,-a[mx],1,n,mx);add2(1,-aa[mx],1,n,mx);
  for i:=2 to n do
  begin
    a1:=wm2(1,1,n,1,mx-1);
    a2:=wm1(1,1,n,mx+1,n);
    if ((mx-1<0) or ((mx-1>0) and (mx+1<n) and (ans+a1>=ans+a2-v[mx]*2))
    or (a1=0)) and (a2<>0) then
    begin inc(ans,a2-v[mx]*2);mx:=pl1;end
    else begin inc(ans,a1);pl1:=pl2;end;
    writeln(ans);
    add1(1,-a[pl1],1,n,pl1);add2(1,-aa[pl1],1,n,pl1);
  end;
end.

  

原文地址:https://www.cnblogs.com/ligen1353055672/p/7732849.html