P1440 求m区间内的最小值

解题思路

这道题目要求我们对于数列中的每一个元素,找到它前面m个元素(不足m个时从第一个开始)的最小值。这是一个典型的滑动窗口最小值问题,可以使用单调队列或线段树来解决。

题目提供的代码使用了线段树解法:

  1. 线段树构建:构建一个能够查询区间最小值的线段树

  2. 查询处理:对于每个元素a[i],查询区间[max(1,i-m), i-1]的最小值

  3. 特殊情况处理:第一个元素前面没有元素,直接输出0

线段树虽然查询时间复杂度为O(logn),但对于n=2e6的数据规模可能不够高效(理论上O(nlogn)可能勉强通过)。更优的解法是使用单调队列,可以达到O(n)的时间复杂度。

代码注释

#include<bits/stdc++.h>
#define lc rt << 1      // 左子节点索引
#define rc rt << 1 | 1  // 右子节点索引
#define lson lc,l,mid   // 左子树参数
#define rson rc,mid + 1,r // 右子树参数
#define ll long long 
using namespace std;

const int N = 2e6 + 10, inf = 0x3f3f3f3f;

// 线段树节点结构体,存储区间最大值(实际用于存最小值)
struct node{
    int maxx;  // 变量名是maxx但实际用于存储最小值
};

node t[N << 2];  // 线段树数组
int n, m;
int a[N];        // 原始数据数组

// 更新父节点的值(取左右子树的最小值)
void pushup(int rt)
{
    t[rt].maxx = min(t[lc].maxx, t[rc].maxx);
}

// 构建线段树
void build(int rt, int l, int r)
{
    if(l == r) {  // 叶子节点
        t[rt].maxx = a[l];  // 存储单个元素值
        return;
    }
    int mid = (l + r) / 2;  // 计算中点
    build(lson);  // 构建左子树
    build(rson);  // 构建右子树
    pushup(rt);   // 更新当前节点
}

// 区间查询最小值
int 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].maxx;  // 完全包含直接返回
    int mid = (l + r) / 2;
    // 返回左右子树查询结果的较小值
    return min(query(lson, x, y), query(rson, x, y));
}

int main()
{
    cin >> n >> m;  // 读取n和m
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);  // 读取数列
    
    build(1, 1, n);  // 构建线段树
    
    cout << 0 << endl;  // 第一个元素前面没有数,输出0
    
    for(int i = 2; i <= n; i++)  // 从第二个元素开始处理
    {
        int x, y;
        if(i < m){  // 前面不足m个元素
            x = 1, y = i - 1;  // 从第1个到前一个
        }
        else {
            x = i - m, y = i - 1;  // 正常情况取前m个
        }
        printf("%d\n", query(1, 1, n, x, y));  // 查询并输出最小值
    }
    return 0;
}

 

posted @ 2025-05-21 09:23  CRt0729  阅读(28)  评论(0)    收藏  举报