• 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);
    	}
    }
    
  • 相关阅读:
    【转】【Linux】linux awk命令详解
    【转】【Linux】Linux 命令行快捷键
    【转】【Linux】sed命令详解
    【转】【Linux】Linux 下zip包的压缩与解压
    【转】【Linux】grep命令详解
    【转】crontab命令 脚本定时运行
    【转】BAT 批处理脚本 教程
    【MySql】脚本备份数据库
    php的json校验json-schema
    phan—php语法静态检查在windows下的配置
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/14590997.html
Copyright © 2020-2023  润新知