参考了这里:http://blog.sina.com.cn/s/blog_6a6aa7830100x890.html
我们从第一条赛道往后选择,设选择到第i条赛道时最优值是f[i],这里注意f[i]表示的方案中第i条是不一定被选中的。
这里的状态转移分两种,第i条选或不选
1:不选,f[i]=f[i-1]
2:选,设连续往前一直选到第j+1条(0<=j<i),则f[i]=f[j]-cost(j+1,i)+pay(j+1,i)
(cost(j+1,i)表示从j+1到i的赛道全部修复的所需付费,即cost[j+1]+cost[j+2]+...+cost[i],pay(j+1,i)表示所有被完全包含在区间[j+1,i]内的比赛赚的钱总和)
这样我们就得到状态转移方程:
f[i]=max{f[i-1],f[j]-cost(j+1,i)+pay(j+1,i)} (0<=j<i)
初始条件f[0]=0
如果朴素转移的话时间是在O(n^2*m),我们用线段树来维护一下就没了。
(没学过线段树优化dp的可以看下面这一段来入门)
假设一个序列v[j]=f[j]-cost(j+1,i)+pay(j+1,i),只要用线段树维护它的区间[0,i-1]最大值就可以了。观察这个表达式,我们可以发现,当i增加1时,所有它之前的v值都要多减去一个cost[i],同时,对于每一个右端点恰好为i,即r=i的比赛(l,r,p),每一个下标小于l的v值都要加上一个p,换句话说,这是一个区间修改,区间查询的问题。完成状态转移后,将f[i]加入线段树的第i位即可。初始时,v序列中只有一个v[0]=0。不过这个v序列我们显然是不需要去再开一个数组来模拟它了,只是为了说得方便引入的。
cf上似乎没开边界检查?算术上溢的时候自然溢出了。
code:
1 program cf115E; 2 type competition=record lb,rb,p:longint; end; 3 var tree:array[0..600000] of record flag,max:int64; end; 4 com:array[0..200010] of competition; 5 cost:array[0..200010] of longint; 6 f:array[0..200010] of int64; 7 i,j,n,m:longint; 8 ans:int64; 9 procedure sort(l,r:longint); 10 var i,j,x:longint; 11 t:competition; 12 begin 13 i:=l; j:=r; x:=com[(l+r)shr 1].rb; 14 repeat 15 while com[i].rb<x do inc(i); 16 while com[j].rb>x do dec(j); 17 if i<=j then 18 begin 19 t:=com[i]; 20 com[i]:=com[j]; 21 com[j]:=t; 22 inc(i); 23 dec(j); 24 end; 25 until i>j; 26 if i<r then sort(i,r); 27 if j>l then sort(l,j); 28 end; 29 function maxf(a,b:int64):int64; 30 begin 31 if a>b then exit(a) else exit(b); 32 end; 33 procedure pushdown(x:longint); 34 begin 35 tree[x+x].max:=tree[x+x].max+tree[x].flag; 36 tree[x+x+1].max:=tree[x+x+1].max+tree[x].flag; 37 tree[x+x].flag:=tree[x+x].flag+tree[x].flag; 38 tree[x+x+1].flag:=tree[x+x+1].flag+tree[x].flag; 39 tree[x].flag:=0; 40 end; 41 procedure edit_len(x,nowl,nowr,l,r,p:longint); 42 var mid:longint; 43 begin 44 if (nowr<l)or(nowl>r) then exit; 45 if (l<=nowl)and(nowr<=r) then 46 begin 47 with tree[x] do 48 begin 49 flag:=flag+p; 50 max:=max+p; 51 end; 52 exit; 53 end; 54 pushdown(x); 55 mid:=(nowl+nowr)shr 1; 56 edit_len(x+x,nowl,mid,l,r,p); 57 edit_len(x+x+1,mid+1,nowr,l,r,p); 58 tree[x].max:=maxf(tree[x+x].max,tree[x+x+1].max); 59 end; 60 procedure edit_point(x,nowl,nowr,posi:longint;p:int64); 61 var mid:longint; 62 begin 63 if nowl=nowr then 64 begin 65 tree[x].flag:=p; 66 tree[x].max:=p; 67 exit; 68 end; 69 pushdown(x); 70 mid:=(nowl+nowr) shr 1; 71 if posi<=mid then edit_point(x+x,nowl,mid,posi,p) 72 else edit_point(x+x+1,mid+1,nowr,posi,p); 73 tree[x].max:=maxf(tree[x+x].max,tree[x+x+1].max); 74 end; 75 function find(x,nowl,nowr,l,r:longint):int64; 76 var mid:longint; 77 begin 78 if (nowr<l)or(nowl>r) then exit(-maxlongint); 79 if (nowl>=l)and(nowr<=r) then exit(tree[x].max); 80 pushdown(x); 81 mid:=(nowl+nowr) shr 1; 82 find:=maxf(find(x+x,nowl,mid,l,r),find(x+x+1,mid+1,nowr,l,r)); 83 end; 84 begin 85 readln(n,m); 86 for i:=1 to n do readln(cost[i]); 87 for i:=1 to m do with com[i] do readln(lb,rb,p); 88 sort(1,m); 89 f[0]:=0; 90 ans:=0; 91 j:=1; 92 for i:=1 to n do 93 begin 94 while com[j].rb=i do 95 begin 96 edit_len(1,0,n,0,com[j].lb-1,com[j].p); 97 inc(j); 98 end; 99 edit_len(1,0,n,0,i-1,-cost[i]); 100 f[i]:=find(1,0,n,0,i-1); 101 if f[i-1]>f[i] then f[i]:=f[i-1]; 102 edit_point(1,0,n,i,f[i]); 103 if f[i]>ans then ans:=f[i]; 104 end; 105 writeln(f[n]); 106 end.