1 条题解
-
0
有长度为 的数组 (编号为 )和窗口大小 ,
你要求出数组中每个长度为 的连续子数组的最小值,你需要按顺序输出这 个值。
数组 将被遍历两次,每遍历到一个元素会将其作为参数 ,调用
helpBessie(val)。你需要实现函数
void helpBessie(int x),你可以用函数void shountMinimum(int x)按顺序汇报答案。特别地,我们并不要求每次helpBessie恰好汇报 次答案。你在函数的两次调用之间只能使用题目提供的寄存器存储 个 位有符号整数,位置编号为 ,最初每个寄存器均存储了 ,用
set(x)存储,get(x)读取,要求set、get的调用总次数不超过 次。。
首先如果没有特殊的空间限制那么就是单调队列模板题,单调队列的空间复杂度是 。
我们发现题目的数组将会被遍历 遍,
那我们应该在第 遍遍历的时候预处理一些信息并存储下来,并在第 遍遍历时求出准确答案。
考虑这些信息需要起到什么作用,要求我们不再需要存储 个单调递增的数,于是我们希望提前知道一些位置的窗口最小值,但是位置不能太多,于是我们考虑根号平衡,每隔 个数存一个答案,一共存下 个。
我们考虑对序列分块,令 ,块编号从 开始,第 个位置所在的块编号为 (为了方便表述我们定义 $id_i=\lfloor\frac{i}{B}\rfloor,L_i=i\cdot B,R_i=(i+1)\cdot B-1$,代码中实际并未定义这些量)。
记我们当前传入的位置为 ,传入的参数为 ,有 ,
在第 遍遍历时求出对于 ,以第 个块的第一个位置 开头的长度为 的区间的最小值的最早位置 。
具体地,我们记录每一块截至 位置的最小值 以及最小值的位置 (所以 后面的块都是 , 只记录了 区间的最小值),分别存储在寄存器的 位置。
设我们当前计算到第 块的 ,若 ,那么此时可以计算 ,我们发现 就是 区间的 中取最小的且 位置最前的,我们将 存储在 (这里的 但是比 大,原因后面讲,是为了方便储存第 遍遍历)。
然后我们在第 遍遍历的时候求答案,
我们维护一个单调队列,头尾指针分别为 ,
我们将当前 加入单调队列,队列中记录每个数的值 以及位置 (值 是单调递增的),分别循环存储在寄存器的 位置,从 循环到 ,其中 定义为单调队列中同时存在的数的数量最大值。
设我们当前算到的起始位置为 ,若有 或 (要么区间长度等于 要么已经出现在 区间的最小值,显然 开头的不可能取到比这更小的值了),那么说明 能取到最小值已经出现,我们将位置 小于 的队列元素弹出,然后输出队列开头的 即可。
下证 不超过 。
在任意时刻,对于当前在计算的起始位置 ,单调队列的元素可以划分为属于 块的元素和属于 块的元素(不妨称为当前块和后续块的集合),
-
对于当前块,块长为 ,在当前块元素单调递增时,块中所有元素都在队列中,最多有 个。
-
对于后继块的集合,若有多于 个后继块集合中的元素(显然这些元素也满足单调递增),我们可以选择只保留元素最小且位置最前的那个,其余的全部弹出。
我们证明为什么只保留 个是正确的,
假设我们同时有两个后继块元素,设这两个数为 ,满足 且 在 前面( 是先前保留的最小元素, 是当前新加入的).
那么当我们计算答案时, 均属于区间 ,
我们发现因为我们预处理了 ,所以当 遍历到 的时候(显然 会先遍历 再遍历 ),有两种情况:
- 若有 ,我们会将 可以贡献到的 全部贡献掉,此时 来到 所在块, 遍历到 时,若 同块,那么 成为了当前块元素,不会被弹出,否则 会作为唯一后继块元素被保留。
- 否则 ,既然 都不是后继块长度为 的区间的最小值,那么 的 更不可能成为后继块的最小值(因为在 失效后会出现比 小的 ,且 在 后面),将 弹出。
综上 不会同时存在。
然后因为 涉及取模操作所以我们取 来提高效率,所以上文取 ,其他就和普通的单调队列是一致的。
时间复杂度是 的,空间复杂度是 的,带 倍常数,可以通过此题。
#define FL(i,a,b) for(int i=(a);i<=(b);i++) #define FR(i,a,b) for(int i=(a);i>=(b);i--) #define ll long long #define ull unsigned long long #define ld long double #define PII pair<int,int> using namespace std; int get(int); void set(int,int); void shoutMinimum(int); int getTrainLength(); int getWindowLength(); int getCurrentCarIndex(); int getCurrentPassIndex(); void helpBessie(int val){ int ps=getCurrentCarIndex(),n=getTrainLength()-1,k=getWindowLength(); int inf=2e9,B=1000,num=n/B,S=1024,C=(S<<1)+1; if(!getCurrentPassIndex()){ if(!ps) FL(i,0,num) set(i,inf); if(val<get(ps/B)) set(ps/B,val),set(ps/B+(num+1),ps); int i=get(5499); while(i<=n/B&&(ps==i*B+k-1||ps==n)){ int mn=inf,pos=0; FL(j,i,ps/B){ int tmp=get(j); if(tmp<mn) mn=tmp,pos=get(j+(num+1)); } set(i+C,pos); i++; } set(5499,i); } else{ if(!ps) set(5496,0),set(5497,1),set(5498,0),set((n/B+1)+C,-1); int i=get(5496),l=get(5497),r=get(5498); while(l<=r&&val<=get(r%S)) r--; r++,set(r%S,val),set(r%S+S,ps); while(i+k-1<=n&&(ps==i+k-1||ps==get((i/B+1)+C))){ while(l<=r&&get(l%S+S)<i) l++; shoutMinimum(get(l%S)); i++; } if(l<=r-1&&ps/B>i/B&&get((r-1)%S+S)/B>i/B) r--; set(5496,i),set(5497,l),set(5498,r); } } -
- 1
信息
- ID
- 6797
- 时间
- 1000ms
- 内存
- 9MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者