P1531 I Hate It
解题思路
这道题目要求我们实现一个可以处理两种操作的数据结构:
-
查询操作(Q):查询区间[a,b]中的最大值
-
更新操作(U):有条件地更新某个位置的值(只有当新值大于原值时更新)
这是一个典型的区间查询与单点更新问题,适合使用线段树来解决。
方法选择
题目提供的代码使用了线段树解法,这是非常合适的选择,因为:
-
线段树可以高效处理区间查询(O(logn)时间)
-
支持单点更新(O(logn)时间)
-
对于n=2e5的数据规模完全适用
其他可能的解法
-
分块处理:将数组分成若干块,预处理每块的最大值
-
查询时间O(√n)
-
更新时间O(√n)
-
实现比线段树简单,但效率较低
-
-
ST表(稀疏表):
-
查询时间O(1)
-
但不支持高效更新,每次更新需要O(nlogn)重建
-
代码注释
#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; // 存储区间最大值 }; node t[N << 2]; // 线段树数组 int n, m; // n-学生数量,m-操作数量 int a[N]; // 学生成绩数组 // 更新父节点的最大值 void pushup(int rt) { t[rt].maxx = max(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 max(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; // 操作参数 scanf("%s%d%d", op, &x, &y); // 读取操作 if(op[0] == 'Q'){ // 查询操作 printf("%d\n", query(1, 1, n, x, y)); } else if(op[0] == 'U' && a[x] < y) // 更新操作(有条件) { a[x] = y; // 更新原数组 change(1, 1, n, x, a[x]); // 更新线段树 } } return 0; }

浙公网安备 33010602011771号