【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.