P1816 忠诚
解题思路
这道题目要求我们处理多个区间最小值查询,属于典型的RMQ(Range Minimum Query)问题。题目中需要处理m笔账目和n个查询,每个查询要求找出[a, b]区间内的最小值。
方法选择
题目提供的代码使用了线段树解法,这是解决RMQ问题的经典方法之一。线段树的主要优势在于:
-
预处理时间O(n)
-
每个查询时间O(logn)
-
支持动态更新(虽然本题不需要)
对于n和m都是1e5的数据规模,线段树的O(n + mlogn)复杂度完全能够胜任。
其他可能的解法
-
ST表(稀疏表):预处理O(nlogn),查询O(1),但不支持动态更新
-
单调队列:适用于滑动窗口最小值问题,但本题不是滑动窗口
-
分块处理:预处理O(n),查询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; // 定义常量,inf表示无穷大 // 线段树节点结构体 struct node{ int maxx; // 虽然变量名是maxx,但实际存储的是最小值 }; node t[N << 2]; // 线段树数组,大小是原数组的4倍 int n, m; // 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); // 更新当前节点的值 } // 修改节点值(本题未使用,但代码保留了这个功能) void change(int rt, int l, int r, int x, int y) { if(r < x || x < l) return; // 超出修改范围 if(l == r){ // 找到目标节点 t[rt].maxx = y; // 更新节点值 return; } int mid = (l + r) / 2; change(lson, x, y); // 修改左子树 change(rson, x, y); // 修改右子树 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; // 读取账目数量和问题数量 for(int i = 1; i <= n; i++) scanf("%d", &a[i]); // 读取账目金额 build(1, 1, n); // 构建线段树 while(m--) // 处理每个查询 { char op[2]; // 未使用的操作符变量(原代码可能考虑后续扩展) int x, y; // 查询区间[x,y] scanf("%d%d", &x, &y); // 读取查询区间 printf("%d ", query(1, 1, n, x, y)); // 查询并输出结果 } return 0; }

浙公网安备 33010602011771号