1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
| #include <iostream> #include <vector> #include <algorithm>
using namespace std;
const int MAXN = 200005; int tree[MAXN * 4]; int scores[MAXN];
void push_up(int node) { tree[node] = max(tree[node << 1], tree[node << 1 | 1]); }
void build(int node, int start, int end) { if (start == end) { tree[node] = scores[start]; return; } int mid = (start + end) >> 1; build(node << 1, start, mid); build(node << 1 | 1, mid + 1, end); push_up(node); }
void update(int node, int start, int end, int idx, int val) { if (start == end) { tree[node] = max(tree[node], val); return; } int mid = (start + end) >> 1; if (idx <= mid) update(node << 1, start, mid, idx, val); else update(node << 1 | 1, mid + 1, end, idx, val); push_up(node); }
int query(int node, int start, int end, int L, int R) { if (L <= start && end <= R) { return tree[node]; } int mid = (start + end) >> 1; int res = 0; if (L <= mid) res = max(res, query(node << 1, start, mid, L, R)); if (R > mid) res = max(res, query(node << 1 | 1, mid + 1, end, L, R)); return res; }
int main() { ios::sync_with_stdio(false); cin.tie(0);
int n, m; while (cin >> n >> m) { for (int i = 1; i <= n; i++) cin >> scores[i]; build(1, 1, n);
for (int i = 0; i < m; i++) { char c; int a, b; cin >> c >> a >> b; if (c == 'Q') { if (a > b) swap(a, b); cout << query(1, 1, n, a, b) << "\n"; } else if (c == 'U') { update(1, 1, n, a, b); } } } return 0; } ````
---
### 4. 复杂度与注意事项
- **时间复杂度**:建树 $O(n)$,每次修改和查询 $O(\log n)$。总计 $O(n + m \log n)$。 - **空间复杂度**:$O(4n)$,主要是 `tree` 数组开销。 - **注意点**: - `ios::sync_with_stdio(false)`:对于 $200,000$ 级别的数据量,传统的 `cin/cout` 会非常慢,建议开启优化或使用 `scanf/printf`。 - **区间合法性**:部分题目测试用例中查询区间 $[a, b]$ 可能出现 $a > b$ 的情况,处理前应做 `swap`。
### 5.线段树核心函数深度解析
线段树的三个核心操作:`build`(建树)、`update`(单点修改)、`query`(区间查询),都遵循递归结构。
---
#### 1. 建树函数 `build`:自底向上的初始化 建树的过程就像是在画一棵倒挂的二叉树。我们从根节点(代表区间 $[1, n]$)开始,不断二分。
* **核心逻辑**: 1. 如果当前区间只有一个数(`start == end`),说明到达了叶子节点,直接赋值。 2. 否则,递归构建左子树和右子树。 3. **PushUp**:子树构建完成后,父节点的值等于两个子节点值的最大值。
**核心代码块:** ```cpp void build(int node, int start, int end) { if (start == end) { tree[node] = scores[start]; return; } int mid = (start + end) >> 1; build(node << 1, start, mid); build(node << 1 | 1, mid + 1, end); tree[node] = max(tree[node << 1], tree[node << 1 | 1]); } ````
---
#### 2. 单点修改 `update`:路径式更新
当你修改了某一个学生的成绩,线段树中从该叶子节点到根节点的路径上,所有父节点的信息都可能失效。
- **核心逻辑**: 1. 通过二分查找,准确定位到代表学生 `idx` 的叶子节点。 2. 更新叶子节点的值。 3. 在递归回溯的过程中,重新计算路径上所有父节点的最大值。
**核心代码块:**
```cpp void update(int node, int start, int end, int idx, int val) { if (start == end) { tree[node] = max(tree[node], val); return; } int mid = (start + end) >> 1; if (idx <= mid) update(node << 1, start, mid, idx, val); else update(node << 1 | 1, mid + 1, end, idx, val); tree[node] = max(tree[node << 1], tree[node << 1 | 1]); }
|