常用演算法題錦——線段樹
阿新 • • 發佈:2021-07-11
線段樹
題目:
單值修改,區間查詢,維護資訊:最大值和最大值的個數
題目描述 牛牛有n個寶石,第i個寶石的價值是w[i]. 有m個操作,操作分為兩種型別 − Change x y 把第x個寶石的價值改成 y − Ask l r 詢問區間[l,r]內寶石的最大價值,和最大價值的寶石有多少個。 輸入描述: 第一行兩個整數 n , m (1 ≤ n,m ≤ 2e5) 第二行有n個整數 w[i] (0 ≤ w[i] ≤ 1e9) 接下來m行,每行代表一個操作。具體見題目描述。 輸出描述: 每次詢問輸出一行,每行兩個整數 val cnt,val代表所有寶石中的最大價值,cnt代表價值最大的寶石有多少個。 示例1 輸入 複製 5 3 2 4 3 6 8 Ask 1 5 Change 2 10 Ask 1 3 輸出 複製 8 1 10 1
程式碼:
#include <bits/stdc++.h> using namespace std; const int N = 200005; int n, m; int w[N]; struct Node{ int l, r; int mx, cnt; }tr[N * 4]; struct Seg_Tree{ void pushup(Node &c, Node &a, Node &b) { if (a.mx == b.mx) c.cnt = a.cnt + b.cnt; else if (a.mx > b.mx) c.cnt = a.cnt; else c.cnt = b.cnt; c.mx = max(a.mx, b.mx); } void pushup(int u) { pushup(tr[u], tr[u << 1], tr[u << 1 | 1]); } void build(int u, int l, int r) { if (l == r) { tr[u] = {l, r, w[l], 1}; return; } tr[u] = {l, r}; int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); pushup(u); } void change(int u, int x, int c) { if (tr[u].l == x && tr[u].r == x) { tr[u] = {x, x, c, 1}; return; } int mid = tr[u].l + tr[u].r >> 1; if (x <= mid) change(u << 1, x, c); if (x > mid) change(u << 1 | 1, x, c); pushup(u); } Node query(int u, int l, int r) { if (tr[u].l >= l && tr[u].r <= r) return tr[u]; int mid = tr[u].l + tr[u].r >> 1; if (r <= mid) return query(u << 1, l, r); else if (l > mid) return query(u << 1 | 1, l, r); else { Node left = query(u << 1, l, r); Node right = query(u << 1 | 1, l, r); Node ans; pushup(ans, left, right); return ans; } } }t; int main(void) { cin >> n >> m; for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]); t.build(1, 1, n); char op[10]; int l, r; while (m -- ) { scanf("%s", op); scanf("%d%d", &l, &r); if (*op == 'C') { t.change(1, l, r); } else { Node x = t.query(1, l, r); printf("%d %d\n", x.mx, x.cnt); } } return 0; }