P4392 [BOI2007]静音问题 题解
Posted begin-ac
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4392 [BOI2007]静音问题 题解相关的知识,希望对你有一定的参考价值。
传送门:P3942
这道题要求我们对所有长度为m的序列,找出其中最大值和最小值的差值不超过c的,并输出它们的起始位置。
看到静态序列最值问题,很自然的想到要用ST表进行Θ(nlogn+n)预处理、Θ(1)查询。但是很不幸,如果直接用两个表分别存储最大值和最小值,最后一个点妥妥的MLE,因此我们需要对空间占用进行优化。
考虑到每个序列的长度是固定的,我们可以先进行一次ST表操作,记录下vali= maxai →ai+m 。再在同一个f数组中进行第二次ST表操作,计算出fi = minai → ai+m,最后1→(n - m)扫描一次各元素,满足条件vali - fi ≤ c时输出i即可。同时,由于序列长度固定为m,所以我们不需要像一般的ST表一样开一个O(nlogn)的数组,开一个O(nlogm)的数组即可存储下全部有效信息,因此第二维可以缩小至 log2m = 15。经过这两次优化,程序的空间复杂度降低至可以接受的程度,可以开心的A掉这道题。完整代码如下:
#include <iostream> #include <cstdio> using namespace std; int n; int val[1000010], f[1000005][15]; inline int max(int x, int y) return x > y ? x : y; inline int min(int x, int y) return x < y ? x : y; inline void init1() for (int i = 1; i <= n; i++) scanf("%d", &f[i][0]);//直接输入,节约空间 for (int j = 1; j <= 14; j++) for(int i = 1; i + (1 << j) - 1 <= n; i++) f[i][j] = max(f[i][j - 1], f[ i + (1 << (j - 1) ) ][j - 1]);//预处理区间最大值 inline void init2() for (int j = 1; j <= 14; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) f[i][j] = min(f[i][j - 1], f[ i + (1 << (j - 1) ) ][j - 1]);//预处理区间最小值 int k; int find1(int l, int r) return max(f[l][k], f[r - (1 << k) + 1][k]);//查询区间最大值 int find2(int l, int r) return min(f[l][k], f[r - (1 << k) + 1][k]);//查询区间最小值 int main() int m, c; scanf("%d%d%d", &n, &m, &c);//按题意输入 init1();//预处理最大值 while( (1 << (k + 1) ) <= m) k++;//计算出区间长度 for (int i = 1, j = i + m - 1; j <= n; i++, j++) val[i] = find1(i, j); //记录下区间最大值 init2();//预处理区间最小值 int ok = 0;//用于记录是否下是否有合法的序列 for(int i = 1, j = i + m - 1; j <= n; i++, j++) if(val[i] - find2(i, j) <= c) ok = printf("%d\n", i); //这里运用了printf函数在成功输出后会返回输出元素个数的性质,只要有输出,ok就会被赋上一个非0值 if(!ok) puts("NONE");//特判没有合法区间的情况 return 0;
以上是关于P4392 [BOI2007]静音问题 题解的主要内容,如果未能解决你的问题,请参考以下文章
题解 P4393 [BOI2007]Sequence 序列问题