NOIP 2017 普及组 跳房子 jump

来源:互联网 发布:菜鸟手机助手源码 编辑:程序博客网 时间:2024/05/17 08:41

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

题解:

这题因为答案是有已知范围[0..xn],所以我们能够去二分答案ans,这个都能想到吧…
时间复杂度:O(log2 xn)
然后我们发现对于每个答案ans’能否满足,我们就可以打个简单的dp,
f[i]表示起点0到第i个位置能得到的最大分数.
f[i]=max{f[j]}+si
j满足max{1,d-ans’}<=xi-xj<=d+ans’
时间复杂度:O(N^2)
当发现有fi≥m则返回true
然后向前二分
反之则向后二分
时间复杂度:O(log2 xn*n^2)
很明显只有50分,

而我们可以发现对于fi我们要找的只是fj的最大值,

因为xi递增,那么我们就可以用单调队列去使这个最大值可以直接得出!

但是单调队列的维护就要注意了,
如果直接去弄的话,那么 xi-xj < max{1,d-ans’} 的情况就会出现,这时候你的单调队列的维护就会出现问题…

所以我们可以用一个j去依次加入fj进入单调队列,
一个i去枚举位置,那么每次我们先将满足的fj加入,
即当max{1,d-ans’}<=xi-xj<=d+ans’则加入,也不要忘了将现在位置已经够不到xi的踢掉!
这样dp就优化到O(N)

时间复杂度就变成了O(log2 xn*n)

var   a:array [0..500001,1..2] of longint;   cmax:array [0..500001] of longint;   f:array [0..500001] of int64;   l,r,ans,mid,i,n,m,d:longint;   rp:boolean;function max(aa,bb:int64):int64;begin   if aa>bb then exit(aa);   exit(bb);end;function need(qd,pd:longint):boolean;var   i,head,tail,j,k:longint;begin   head:=1; tail:=1;   f[0]:=0; cmax[1]:=0;   for k:=1 to n+1 do      if (a[k,1]<=pd) and (a[k,1]>=qd) then break;   j:=k;   for i:=k to n do     begin        f[i]:=-1;          while (head<=tail) and (a[cmax[head],1]<a[i,1]-pd) do inc(head);          while (j<i) and ((a[i,1]-pd>a[j,1]) or (f[j]=-1)) do inc(j);          while (j<i) and (a[i,1]-qd>=a[j,1]) do          begin               while (head<=tail) and (f[j]>=f[cmax[tail]]) do dec(tail);               inc(tail);               cmax[tail]:=j;               inc(j);          end;          if head<=tail then          begin               f[i]:=a[i,2]+f[cmax[head]];               if f[i]>=m then exit(true);          end;     end;  exit(false);end;begin     readln(n,d,m);     a[0,1]:=0; a[0,2]:=0;     for i:=1 to n do       readln(a[i,1],a[i,2]);     l:=0; r:=a[n,1];     ans:=-1;     while l<=r do     begin          mid:=(l+r) div 2;          rp:=need(max(1,d-mid),d+mid);              if rp then begin ans:=mid; r:=mid-1 end                    else l:=mid+1;     end;     writeln(ans);end.