LOJ2870「USACO 2018 US Open Platinum」Train Tracking【分块】

这是一道交互题

给定正整数 (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);
	}
}
原文地址:https://www.cnblogs.com/AThousandMoons/p/14590997.html