Hướng giải của Chmin Đoạn Và Tính Tổng
Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Tác giả:
Hướng dẫn giải
Bài toán cập nhật min đoạn: \(A[i] = \min(A[i], X)\) và truy vấn tổng đoạn. Ta sử dụng cấu trúc Segment Tree Beats:
- Tại mỗi node quản lý:
sum(tổng),mx(giá trị lớn nhất),smx(giá trị lớn thứ nhì, nhỏ hơnmx), vàmx_cnt(số lượng phần tử đạt giá trịmx). - Khi cập nhật giá trị chmin với \(X\) trên đoạn:
- Nếu
tree[node].mx <= X: dừng lại (không ảnh hưởng). - Nếu
tree[node].smx < X < tree[node].mx: cập nhậtsum -= (mx - X) * mx_cnt, gánmx = Xvà dừng lại (lúc này chỉ các phần tử lớn nhất bị biến đổi). - Ngược lại: đẩy nhãn lười xuống 2 con và đệ quy sâu xuống.
- Nếu
Độ phức tạp trung bình đã được chứng minh là \(O(\log N)\) mỗi thao tác.
Mã nguồn C++ mẫu
#include <bits/stdc++.h>
using namespace std;
struct Node {
long long sum; int mx, smx, mx_cnt;
};
class SegTreeBeats {
int n; vector<Node> tree;
void combine(int node) {
int l = 2*node, r = 2*node+1;
tree[node].sum = tree[l].sum + tree[r].sum;
if (tree[l].mx == tree[r].mx) {
tree[node].mx = tree[l].mx;
tree[node].mx_cnt = tree[l].mx_cnt + tree[r].mx_cnt;
tree[node].smx = max(tree[l].smx, tree[r].smx);
} else if (tree[l].mx > tree[r].mx) {
tree[node].mx = tree[l].mx;
tree[node].mx_cnt = tree[l].mx_cnt;
tree[node].smx = max(tree[l].smx, tree[r].mx);
} else {
tree[node].mx = tree[r].mx;
tree[node].mx_cnt = tree[r].mx_cnt;
tree[node].smx = max(tree[l].mx, tree[r].smx);
}
}
void push_chmin(int node, int val) {
if (tree[node].mx <= val) return;
tree[node].sum -= (long long)(tree[node].mx - val) * tree[node].mx_cnt;
tree[node].mx = val;
}
void push(int node) {
push_chmin(2*node, tree[node].mx);
push_chmin(2*node+1, tree[node].mx);
}
public:
SegTreeBeats(int size) { n = size; tree.assign(4 * n, {0, -1, -1, 0}); }
void build(int node, int start, int end, const vector<int>& a) {
if (start == end) { tree[node] = {(long long)a[start], a[start], -1, 1}; return; }
int mid = (start + end)/2;
build(2*node, start, mid, a);
build(2*node+1, mid+1, end, a);
combine(node);
}
void update_chmin(int node, int start, int end, int l, int r, int val) {
if (r < start || end < l || tree[node].mx <= val) return;
if (l <= start && end <= r && tree[node].smx < val) {
push_chmin(node, val);
return;
}
push(node);
int mid = (start + end)/2;
update_chmin(2*node, start, mid, l, r, val);
update_chmin(2*node+1, mid+1, end, l, r, val);
combine(node);
}
long long query_sum(int node, int start, int end, int l, int r) {
if (r < start || end < l) return 0;
if (l <= start && end <= r) return tree[node].sum;
push(node);
int mid = (start + end)/2;
return query_sum(2*node, start, mid, l, r) + query_sum(2*node+1, mid+1, end, l, r);
}
};
int main(){
ios_base::sync_with_stdio(false); cin.tie(NULL);
int n, q; if (!(cin >> n >> q)) return 0;
vector<int> a(n);
for (int i=0; i<n; i++) cin >> a[i];
SegTreeBeats st(n);
st.build(1, 0, n-1, a);
while (q--) {
int t, l, r; cin >> t >> l >> r;
l--; r--;
if (t == 1) { int x; cin >> x; st.update_chmin(1, 0, n-1, l, r, x); }
else { cout << st.query_sum(1, 0, n-1, l, r) << "\n"; }
}
}
Nhận xét