先决条件:段树,段树中的延迟传播。
给定N个整数的数组arr []。任务是执行以下操作:
- 将值arr [i]更改为min(arr [i],X) ,其中X是给定范围[L,R]的整数。
- 在对上面的数组进行更新之前和之后,找到从索引L到R的最大值,其中0≤L≤R≤N-1。
- 在给上述数组进行更新之前和之后,找到从索引L到R的元素的总和,其中0≤L≤R≤N-1。
例子:
Input: arr[] = {1, 2, 3, 4, 5}, L = 2, R = 4, X = 3
Output:
Maximum in range [2, 4] before update: 5
Sum in range [2, 4] before update: 12
Maximum in range [2, 4] after update: 3
Sum in range [2, 4] after update: 9
Explanation:
Before Update:
arr[] = {1, 2, 3, 4, 5}
The maximum value from [L, R] is 5
Sum in range [L, R] is 3 + 4 + 5 = 12
After Update:
arr[] = {1, 2, 3, 3, 3}
The maximum value from [L, R] is 3
Sum in range [L, R] is 3 + 3 + 3 = 9
Input: arr[] = {1, 4, 19, 0, 7, 22, 7}, L = 1, R = 5, X = 14
Output:
Maximum in range [1, 5] before update: 22
Sum in range [1, 5] before update: 52
Maximum in range [1, 5] after update: 22
Sum in range [1, 5] after update: 39
Explanation:
Before Update:
arr[] = {1, 4, 19, 0, 7, 22, 7}
The maximum value from [L, R] is 22
Sum in range [L, R] is 4 + 19 + 0 + 7 + 22 = 52
After Update:
arr[] = {1, 4, 14, 0, 7, 14, 7}
The maximum value from [L, R] is 14
Sum in range [L, R] is 4 + 14 + 0 + 7 + 14 = 39
方法:
懒散传播的几个术语:
- pushdown():惰性标签应用于当前节点,然后向下推送至其子节点。
- tag_condition:设置惰性节点需要满足的条件。在正常的懒树中,通常情况下,节点覆盖范围完全位于更新范围内。
- 段树中的节点代表数组的范围。现在,该范围内的所有元素将具有不同的值,并且在更新期间它们将以不同的量变化。因此,我们需要有关该范围内不同值及其计数的信息。因此,这成为最坏情况下的O (N)操作,如一个范围内最多N个不同的节点。
- 以下是解决细分树中这些限制的方法。段树中的每个节点将具有以下值:
- value :一个范围的值,这里是该范围内所有元素的总和
- maxval :该范围内的最大值
- secondmax :该范围内的严格第二最大值
- cnt_max :该范围内的最大值计数
- cnt_secondmax :该范围内的secondmax的计数
步骤如下:
- 为每个节点具有上述属性的给定数组元素形成一个Segment Tree。
- 对于updateQuery()调用,请执行以下操作:
- 如果max_value小于更新的值(例如X ),则不会更改任何值,因为在这种情况下min(arr [i],X)本身就是arr [i]。
- 如果secondmax≤X≤maxvalue ,则更新该节点的值,并且newSum将通过以下方式计算:
new_sum = sum - f*maxvalue + f*X, where f is the frequency of maxvalue
- 查询以找到[L,R]范围内的最大数字:
- 如果范围[L,R]完全超出范围,则返回0 。
- 如果范围[L,R]完全位于范围内,则当前范围的最大值为tree [pos] .mx1 。
- 否则,递归检查左右子树的上述条件。
- 上述所有递归调用的最大值都在[L,R]范围内给出最大值。
- 查询以找到[L,R]范围内的总和:
- 如果范围[L,R]完全超出范围,则返回0。
- 如果范围[L,R]完全位于范围内,则当前范围的总和为tree [pos] .sum 。
- 否则,递归检查左右子树的上述条件。
- 上述递归调用的所有值的总和得出范围为[L,R]的总和。
下面是上述方法的实现:
// C++ program for the above approach
#include
using namespace std;
// Node for each Segment Tree
typedef struct node {
int sum;
int mx1;
int mx2;
int cnt_mx1;
int cnt_mx2;
// Constructor
node()
{
sum = mx1 = mx2 = 0;
cnt_mx1 = cnt_mx2 = 0;
}
} node;
const int N = 1e5 + 5;
// Segment Tree Node
node tree[N];
// For Lazy Propagation
int lazy[N];
// Function to merge 2 nodes of Segment
// Tree
void combine(int pos)
{
// Map to store the count of
// maximum1 and maximum2 for every
// node segment tree
map x;
// Update the count for left and
// right subtree
x[tree[2 * pos + 1].mx1] += tree[2 * pos + 1].cnt_mx1;
x[tree[2 * pos + 1].mx2] += tree[2 * pos + 1].cnt_mx2;
x[tree[2 * pos + 2].mx1] += tree[2 * pos + 2].cnt_mx1;
x[tree[2 * pos + 2].mx2] += tree[2 * pos + 2].cnt_mx2;
// Vector pair to store mx1 & mx2
vector > v;
// Traverse the v
for (auto it = x.begin(); it != x.end(); it++) {
v.push_back({ it->first, it->second });
}
int n = v.size();
// Update the mx1 and mx2 after
// combined node
tree[pos].mx1 = v[n - 1].first;
tree[pos].cnt_mx1 = v[n - 1].second;
// If only one node
if (n == 1) {
tree[pos].mx2 = tree[pos].cnt_mx2 = 0;
}
// ELse Update mx2 and cnt_mx2
else {
tree[pos].mx2 = v[n - 2].first;
tree[pos].cnt_mx2 = v[n - 2].second;
}
// Update the sum
tree[pos].sum = tree[2 * pos + 1].sum
+ tree[2 * pos + 2].sum;
}
// Function that returns true if tag
// condition is satisfied, and we can
// do lazy update
bool tag_condition(int pos, int x)
{
if (tree[pos].mx1 > x
&& tree[pos].mx2 <= x) {
return true;
}
return false;
}
// Function that pushes down the lazy
// value of the current node to its children
void pushdown(int beg, int end, int pos)
{
// If tag condition satisfies
if (tag_condition(pos, lazy[pos])) {
int initsum = tree[pos].mx1 * tree[pos].cnt_mx1;
int finsum = lazy[pos] * tree[pos].cnt_mx1;
tree[pos].sum += finsum - initsum;
// If only one node, then update the
// maximum value to current position
if (beg == end)
tree[pos].mx1 = lazy[pos];
// If lazy[pos] > maximum value
else {
// Update mx1 to current
// lazy[pos]
if (lazy[pos] > tree[pos].mx2)
tree[pos].mx1 = lazy[pos];
// Else update the count
else {
tree[pos].mx1 = lazy[pos];
tree[pos].cnt_mx1 += tree[pos].cnt_mx2;
tree[pos].mx2 = tree[pos].cnt_mx2 = 0;
// map to store the cnt
// of maximum1 and maximum2
// for every node in segment
// tree
map x;
x[tree[2 * pos + 1].mx1] += tree[2 * pos + 1].cnt_mx1;
x[tree[2 * pos + 1].mx2] += tree[2 * pos + 1].cnt_mx2;
x[tree[2 * pos + 2].mx1] += tree[2 * pos + 2].cnt_mx1;
x[tree[2 * pos + 2].mx2] += tree[2 * pos + 2].cnt_mx2;
// Traverse the map
for (auto it = x.begin(); it != x.end(); it++) {
// Update the maximum
// count
if (it->first != tree[pos].mx1
&& it->first > tree[pos].mx2) {
tree[pos].mx2 = it->first;
tree[pos].cnt_mx2 = it->second;
}
}
}
// Update the value for
// lazy left and right
// subtree
lazy[2 * pos + 1] = min(lazy[2 * pos + 1],
lazy[pos]);
lazy[2 * pos + 2] = min(lazy[2 * pos + 2],
lazy[pos]);
}
}
lazy[pos] = INT_MAX;
}
// Function that Lazy update in segment
// tree i.e., arr[i] = min(arr[i], val)
void update(int beg, int end, int l, int r,
int pos, int val)
{
// Push the current node value to
// left and right subtree
if (lazy[pos] < INT_MAX)
pushdown(beg, end, pos);
// If inside the range, then update
// the value as per the conditions
if (l <= beg and r >= end
&& tag_condition(pos, val)) {
lazy[pos] = min(lazy[pos], val);
pushdown(beg, end, pos);
return;
}
// Outside the range
else if (l > end || r < beg
|| beg > end
|| tree[pos].mx1 <= val) {
return;
}
// Check for left and right subtree
else {
int mid = (beg + end) / 2;
update(beg, mid, l, r,
2 * pos + 1, val);
update(mid + 1, end, l, r,
2 * pos + 2, val);
combine(pos);
}
}
// Function that returns the maximum
// value in range [L, R]
int query1(int beg, int end, int l,
int r, int pos)
{
// Push the current node value in
// the left and right subtree
if (lazy[pos] < INT_MAX) {
pushdown(beg, end, pos);
}
// If inside the range, then return
// the maximum value
if (l <= beg && r >= end) {
return tree[pos].mx1;
}
// Outside the range
else if (l > end || r < beg
|| beg > end) {
return 0;
}
// Check for left and right subtree
else {
int mid = (beg + end) / 2;
return max(query1(beg, mid, l, r,
2 * pos + 1),
query1(mid + 1, end, l,
r, 2 * pos + 2));
}
}
// Function to find the sum in the
// range [L, R]
int query2(int beg, int end, int l,
int r, int pos)
{
// Push the current node value
if (lazy[pos] < INT_MAX)
pushdown(beg, end, pos);
// If in the range, return the
// sum
if (l <= beg and r >= end)
return tree[pos].sum;
else if (l > end || r < beg
|| beg > end) {
return 0;
}
// Check for left and right subtree
else {
int mid = (beg + end) / 2;
return query2(beg, mid, l, r,
2 * pos + 1)
+ query1(mid + 1, end, l,
r, 2 * pos + 2);
}
}
// Construct Segment Tree
void constr(int arr[], int beg,
int end, int pos)
{
// If only a single node
if (beg == end) {
int x = arr[beg];
tree[pos].sum = x;
tree[pos].mx1 = x;
tree[pos].cnt_mx1 = 1;
tree[pos].mx2 = 0;
tree[pos].cnt_mx2 = 0;
return;
}
// Recursively update for
// left and right subtree
else {
int mid = (beg + end) / 2;
// For Left subtree
constr(arr, beg, mid, 2 * pos + 1);
// For right subtree
constr(arr, mid + 1, end, 2 * pos + 2);
// Combine the two left and
// right subtree
combine(pos);
}
}
// A utility function to construct
// the segment tree
void construct(int arr[], int n)
{
for (int i = 0; i < N; i++) {
lazy[i] = INT_MAX;
}
// Function call to Construct
// segment tree
constr(arr, 0, n - 1, 0);
}
// Driver Code
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
// Construct segment tree
construct(arr, 5);
cout << "Maximum in [2, 4] before update: ";
// Query for maximum in range [0, 4]
cout << query1(0, 4, 2, 4, 0) << endl;
cout << "Sum in [2, 4] before update: ";
// Query for sum in range [0, 4]
cout << query2(0, 4, 2, 4, 0) << endl;
// Update Query
update(0, 4, 2, 5, 0, 3);
cout << endl;
cout << "Updated array elements between "
<< "[2, 4] as min(arr[i], 3)" << endl;
cout << endl;
cout << "Maximum in [2, 4] after update: ";
// Query for maximum in range [0, 4]
cout << query1(0, 4, 2, 4, 0) << endl;
cout << "Sum in [2, 4] after update: ";
// Query for maximum in range [0, 4]
cout << query2(0, 4, 2, 4, 0) << endl;
return 0;
}
Maximum in [2, 4] before update: 5
Sum in [2, 4] before update: 8
Updated array elements between [2, 4] as min(arr[i], 3)
Maximum in [2, 4] after update: 3
Sum in [2, 4] after update: 6
时间复杂度: O(N * log N)