P1886 滑动窗口 /【模板】单调队列
解题思路
这道题目是经典的滑动窗口最值问题,要求在一个长度为n的序列中,对于每个长度为k的滑动窗口,求出窗口中的最小值和最大值。
方法选择
题目提供的代码使用了线段树解法,这是可行的但并非最优解。线段树的主要特点包括:
-
预处理时间O(n)
-
每个查询时间O(logn)
-
可以处理动态更新的情况
但对于滑动窗口这种特殊问题,更优的解法是使用单调队列:
-
单调队列解法:
-
预处理时间O(n)
-
每个查询时间O(1)(均摊)
-
空间复杂度O(n)
-
特别适合滑动窗口这类固定窗口大小的问题
-
线段树解法的优缺点
优点:
-
代码结构清晰
-
可以处理更一般的区间查询问题
-
支持动态更新
缺点:
-
对于滑动窗口这种特殊问题,时间复杂度不如单调队列优秀
-
空间消耗较大(需要4倍原始数组空间)
#include<bits/stdc++.h> #define ll long long #define lc rt << 1 // 左子节点索引 #define rc rt << 1 | 1 // 右子节点索引 #define lson lc,l,mid // 左子树参数 #define rson rc,mid + 1,r // 右子树参数 using namespace std; const int N = 1e6 + 10, inf = 0x3f3f3f3f; // 线段树节点结构体 struct node{ ll sum; // 区间和(本问题中未使用) ll minn; // 区间最小值 ll maxx; // 区间最大值 }; node t[N << 2]; // 线段树数组 ll n, m; // n-序列长度,m-窗口大小 ll a[N]; // 原始序列 // 更新父节点信息 void pushup(int rt) { t[rt].sum = t[lc].sum + t[rc].sum; // 区间和(未使用) t[rt].minn = min(t[lc].minn, t[rc].minn); // 区间最小值 t[rt].maxx = max(t[lc].maxx, t[rc].maxx); // 区间最大值 } // 构建线段树 void build(int rt, int l, int r) { if(l == r){ // 叶子节点 t[rt].sum = a[l]; t[rt].minn = t[rt].maxx = a[l]; // 单个元素的min和max就是它本身 return; } int mid = (l + r) >> 1; // 计算中点 build(lson); // 构建左子树 build(rson); // 构建右子树 pushup(rt); // 更新当前节点信息 } // 查询区间最小值 ll query(int rt, int l, int r, int x, int y) { if(r < x || y < l) return inf; // 区间无交集,返回极大值 if(x <= l && r <= y) return t[rt].minn; // 完全包含,直接返回最小值 int mid = (l + r) / 2; // 返回左右子树查询结果的较小值 return min(query(lson, x, y), query(rson, x, y)); } // 查询区间最大值 ll query2(int rt, int l, int r, int x, int y) { if(r < x || y < l) return -inf; // 区间无交集,返回极小值 if(x <= l && r <= y) return t[rt].maxx; // 完全包含,直接返回最大值 int mid = (l + r) / 2; // 返回左右子树查询结果的较大值 return max(query2(lson, x, y), query2(rson, x, y)); } int main() { cin >> n >> m; // 读取序列长度和窗口大小 for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); // 读取序列 build(1, 1, n); // 构建线段树 // 输出每个窗口的最小值 for(int i = m; i <= n; i++) { int x = i - m + 1, y = i; // 计算窗口位置[x,y] printf("%lld ", query(1, 1, n, x, y)); // 查询并输出最小值 } cout << endl; // 输出每个窗口的最大值 for(int i = m; i <= n; i++) { int x = i - m + 1, y = i; // 计算窗口位置[x,y] printf("%lld ", query2(1, 1, n, x, y)); // 查询并输出最大值 } return 0; }

浙公网安备 33010602011771号