【codevs3012+codevs3037】线段覆盖4+线段覆盖5(DP)

  线段覆盖4网址:http://codevs.cn/problem/3012/

  线段覆盖5网址:http://codevs.cn/problem/3037/

  题目大意:给出一条直线上的一坨线段,每条线段有权值,在这一坨线段中取一小坨线段,使他们的不相交并且权值和最大。

  显然,线段覆盖4和5的差别就是线段的长度,所以这两道题完全可以一起A掉。

  从线段覆盖1做到线段覆盖5,唯一没有变的思想是:按照线段的某一个端点的位置排序,再进行贪心或DP。所以,我们还是一样先按照右端点的位置从左到右排个序。然后,我们就能DP了(f[i]表示以线段i为最后选择的线段时的最大权值和,则f[i]=max(f[j])+第i条线段的权值(1<=j<i,且线段i,j不相交)。

  不过且慢,如果在决策时直接枚举在第i个点之前的所有点,这样的时间复杂度是O(n^2)。而对于n<=10^6的数据,我们需要的是一种O(n log n)的解法。我们可以发现,按照右端点排序后的两条线段i,j(设i排在j后面),如果它们不相交,需要满足i的左端点>=j的右端点。线段i的左端点在这轮决策中是确定的,而右端点已近排好了序,满足单调性,所以,我们可以通过二分来决策一下。不过要注意,因为我们还要保持已决策答案的单调性,所以我们需要的是一个类似单调栈的东西来存放f数组,在当前决策出的结果大于栈顶的结果时,才把它入栈。最后,栈顶就是答案。

var f,a,b,c,w,y:array[0..1000010]of int64;
  n,i,j,t,l,r,m:longint;
  ans:int64;
procedure qs(l,r:longint);
var i,j:longint;
  m,t:int64;
begin
  i:=l; j:=r; m:=b[(l*3+r)>>2];
  repeat
    while b[i]<m do inc(i);
    while b[j]>m do dec(j);
    if i<=j then begin
      t:=a[i]; a[i]:=a[j]; a[j]:=t;
      t:=b[i]; b[i]:=b[j]; b[j]:=t;
      t:=c[i]; c[i]:=c[j]; c[j]:=t;
      inc(i); dec(j);
    end;
  until i>j;
  if l<j then qs(l,j);
  if i<r then qs(i,r);
end;
begin
  read(n);
  for i:=1 to n do
    read(a[i],b[i],c[i]);
  qs(1,n);
  t:=1; y[1]:=b[1]; w[1]:=c[1]; f[1]:=c[1];
  for i:=2 to n do begin
    l:=0; r:=t+1;
    while l+1<r do begin
      m:=(l+r)>>1;
      if y[m]>a[i] then r:=m
      else l:=m;
    end;
    f[i]:=w[l]+c[i];
    if f[i]>w[t] then begin
      inc(t); y[t]:=b[i]; w[t]:=f[i];
    end;
  end;
  writeln(w[t]);
end.
View Code

 

posted @ 2017-02-18 00:16  QuartZ_Z  阅读(510)  评论(0编辑  收藏  举报