📜  更新前后[L,R]中数组中元素的总和和最大值

📅  最后修改于: 2021-05-17 20:39:18             🧑  作者: Mango

先决条件:段树,段树中的延迟传播。
给定N个整数的数组arr []。任务是执行以下操作:

  1. 将值arr [i]更改为min(arr [i],X) ,其中X是给定范围[L,R]的整数。
  2. 在对上面的数组进行更新之前和之后,找到从索引L到R的最大值,其中0≤L≤R≤N-1。
  3. 在给上述数组进行更新之前和之后,找到从索引L到R的元素的总和,其中0≤L≤R≤N-1。

例子:

方法:
懒散传播的几个术语:

  1. pushdown():惰性标签应用于当前节点,然后向下推送至其子节点。
  2. tag_condition:设置惰性节点需要满足的条件。在正常的懒树中,通常情况下,节点覆盖范围完全位于更新范围内。
  3. 段树中的节点代表数组的范围。现在,该范围内的所有元素将具有不同的值,并且在更新期间它们将以不同的量变化。因此,我们需要有关该范围内不同值及其计数的信息。因此,这成为最坏情况下的O (N)操作,如一个范围内最多N个不同的节点。
  4. 以下是解决细分树中这些限制的方法。段树中的每个节点将具有以下值:
    • value :一个范围的值,这里是该范围内所有元素的总和
    • maxval :该范围内的最大值
    • secondmax :该范围内的严格第二最大值
    • cnt_max :该范围内的最大值计数
    • cnt_secondmax :该范围内的secondmax的计数

步骤如下:

  1. 为每个节点具有上述属性的给定数组元素形成一个Segment Tree。
  2. 对于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
      
  3. 查询以找到[L,R]范围内的最大数字:
    • 如果范围[L,R]完全超出范围,则返回0
    • 如果范围[L,R]完全位于范围内,则当前范围的最大值为tree [pos] .mx1
    • 否则,递归检查左右子树的上述条件。
    • 上述所有递归调用的最大值都在[L,R]范围内给出最大值。
  4. 查询以找到[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)