这是一道交互题
给定正整数 (n,k),按顺序输入长为 (n) 的自然数序列 (c_i) 两次,要求在仅使用 (5500) 个 ( ext{int}) 的数组 (S) 的情况下,按顺序输出所有长为 (k) 的区间的最小值。你需要实现以下函数:
- ( exttt{void helpBessie(int x)}):
- (x) 表示当前输入的数。
- 你不能定义任何非常量的全局或静态变量。
当然 OJ 上好像限制不了
你可以调用以下函数:
- ( exttt{int get(int index)}):获取 (S[index]) 的值。
- ( exttt{void set(int index, int value)}):令 (S[index]:=value)。
- ( exttt{void shoutMinimum(int output)}):表示输出 (output) 这个数。
- ( exttt{int getTrainLength()}):获取 (n) 的值。
- ( exttt{int getWindowLength()}):获取 (k) 的值。
- ( exttt{int getCurrentCarIndex()}):获取当前输入的数的下标。
- ( exttt{int getCurrentPassIndex()}):若当前是
初见则返回 (0),否则返回 (1)。
你至多能调用 ( exttt{set,get}) 共 (2.5cdot 10^7) 次。(kle nle 10^6,c_ile 10^9, ext{ML}=9 ext{MiB})。
这是经典的滑动窗口问题,(O(n)) 空间的单调队列做法大家已经熟知。
在读入两遍的情况下,有一个只需 (3sqrt n+O(1)) 个 ( exttt{int}) 的分块做法。
设 (f_i) 表示 (c_i,c_{i+1},cdots,c_{i+k-1}) 的最小值的下标,待定阈值 (B)。
初见的时候求出 (f_0,f_B,cdots,f_{Blfloor (n-k)/B floor}),这个只用在做单调队列的时候改一改,若当前点不是 (B) 的倍数且不比队尾优就不管它,于是单调队列只用塞 (frac nB) 个元素。
第二次的时候按块做,设当前在处理 ([iB,(i+1)B)),则 (<f_{iB}) 的元素不用考虑,([f_{iB},(i+1)B)) 这些点塞进单调队列里,([(i+1)B,(i+1)B+k)) 这些点一旦被加进来就不会弹出,开一个变量记录这个区间内当前已加入点的最小值。
需要注意的是,如果做下一块的时候又要把 (f_{(i+1)B}) 之后的点塞进单调队列重做一遍,肯定是不行的。
但如果当前已经输入到了 (f_{(i+1)B}) 这个位置,((i+1)B) 之前的答案就已经确定了,把答案输出之后直接紧接着做下一块即可。
取 (B=sqrt n),时间复杂度 (O(n))。
#include"grader.h"
#define ans fr==re?nowv:min(nowv,get(fr))
const int B = 1000, INF = 0x7fffffff;
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
template<typename T>
T min(const T &a, const T &b){if(a < b) return a; return b;}
void helpBessie(int v){
int n = getTrainLength(), k = getWindowLength(), id = getCurrentCarIndex();
if(getCurrentPassIndex()){
int pr = get(2*B); if(id < pr) return;
int fr = get(3*B), re = get(3*B+1), nowb = get(3*B+2), nowp = get(3*B+3), nowv = get(3*B+4), L = id-k+1;
if(id == pr){fr = re = nowb = nowp = 0; nowv = INF;}
if(id < (nowb+1)*B){
while(fr < re && v <= get(re-1)) --re;
set(re, v); set(re+B, id); ++re;
} else chmin(nowv, v);
if(L == nowp){while(fr < re && get(fr+B) < L) ++fr; shoutMinimum(ans); ++nowp;}
while(nowb < (n-k)/B && id == get(2*B+nowb+1)){
while(nowp <= (nowb+1)*B){
while(fr < re && get(fr+B) < nowp) ++fr;
shoutMinimum(ans); ++nowp;
} ++nowb; fr = 0; re = 1; nowv = INF; set(0, v); set(B, id);
} set(3*B, fr); set(3*B+1, re); set(3*B+2, nowb); set(3*B+3, nowp); set(3*B+4, nowv);
} else {
int fr = get(3*B), re = get(3*B+1), L = id-k+1;
if(!(id % B) || v <= get(re-1)){
while(fr < re && v <= get(re-1)) --re;
set(re, v); set(re+B, id); ++re;
} if(L >= 0 && !(L % B)){
while(fr < re && get(fr+B) < L) ++fr;
set(L/B + B*2, get(fr+B));
} set(3*B, fr); set(3*B+1, re);
}
}