这是一道题意简单,数据较大的题(喜闻乐见);
一开始可能会想到RMQ问题,ST,线段树都是O(nlogn),应该勉强能过(没试过);
由于这道题区间是滚动连续的,所以,可以使用单调队列!
以最小值为例:
对于a[i],a[j],i>j;
如果a[i]<a[j],那么在加入i到滚动窗口中,找最小值一定不用考虑a[j];
因为a[j]比a[i]要先退出窗口,且即便i,j都在窗口中,那么a[j]一定不是最小值(a[i]<a[j]);
所以我们维护一个单调升序队列,对于新加入的元素,如果比队尾元素大,那么在队尾处入队(可能成为某个区间最小值);
否则的话,找打一个合适的位置m使之前位置的树比它小,后面的数比它大,同时将位置m后面的数全部退队;
由于单调队列是随着下标递增的,那么对于滚动窗口滚出的元素,从队头判断即可。
1 var ans:array[0..1000000,1..2] of longint; 2 a,w:array[0..1000000] of longint; 3 h,f,r,mid,m,n,i,v:longint; 4 begin 5 readln(n,m); 6 for i:=1 to n do 7 read(a[i]); 8 a[0]:=-2147483647; 9 for i:=1 to n do 10 begin 11 if a[i]>a[w[h]] then 12 begin 13 h:=h+1; 14 w[h]:=i; 15 end 16 else begin 17 f:=1; 18 r:=h; 19 repeat 20 mid:=(f+r) div 2; 21 if (a[w[mid]]>=a[i]) and (a[w[mid-1]]<a[i]) then break; 22 if (a[w[mid]]>=a[i]) then r:=mid-1 else f:=mid+1; 23 until f>r; 24 h:=mid; 25 w[h]:=i; 26 end; 27 if i>=m then 28 begin 29 f:=1; 30 r:=h; 31 v:=0; 32 repeat 33 mid:=(f+r) div 2; 34 if w[mid]<i-m+1 then f:=mid+1; 35 if w[mid]>=i-m+1 then 36 begin 37 v:=mid; 38 r:=mid-1; 39 end; 40 until f>r; 41 ans[i-m+1,1]:=a[w[v]]; 42 end; 43 end; 44 a[0]:=2147483647; 45 h:=0; 46 fillchar(w,sizeof(w),0); 47 for i:=1 to n do 48 begin 49 if a[i]<a[w[h]] then 50 begin 51 h:=h+1; 52 w[h]:=i; 53 end 54 else begin 55 f:=1; 56 r:=h; 57 repeat 58 mid:=(f+r) div 2; 59 if (a[w[mid]]<=a[i]) and (a[w[mid-1]]>a[i]) then break; 60 if (a[w[mid]]<=a[i]) then r:=mid-1 else f:=mid+1; 61 until f>r; 62 h:=mid; 63 w[h]:=i; 64 end; 65 if i>=m then 66 begin 67 f:=1; 68 r:=h; 69 v:=0; 70 repeat 71 mid:=(f+r) div 2; 72 if w[mid]<i-m+1 then f:=mid+1; 73 if w[mid]>=i-m+1 then 74 begin 75 v:=mid; 76 r:=mid-1; 77 end; 78 until f>r; 79 ans[i-m+1,2]:=a[w[v]]; 80 end; 81 end; 82 for i:=1 to n-m+1 do 83 write(ans[i,1],' '); 84 writeln; 85 for i:=1 to n-m+1 do 86 write(ans[i,2],' '); 87 writeln; 88 end.
单调队列,关键在于看出题目中的单调性;
还有单调队列一般存放标号比较方便