传送门:P3942
这道题要求我们对所有长度为m的序列,找出其中最大值和最小值的差值不超过c的,并输出它们的起始位置。
看到静态序列最值问题,很自然的想到要用ST表进行Θ(nlogn+n)预处理、Θ(1)查询。但是很不幸,如果直接用两个表分别存储最大值和最小值,最后一个点妥妥的MLE,因此我们需要对空间占用进行优化。
考虑到每个序列的长度是固定的,我们可以先进行一次ST表操作,记录下vali= max{ai →ai+m} 。再在同一个f数组中进行第二次ST表操作,计算出fi = min{ai → 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; }
原文地址:https://www.cnblogs.com/begin-AC/p/11385694.html
时间: 2024-10-11 08:18:23