5

have given an array initially having some Max.val value , then there are queries making a update in a Range L,R, such that the value at any position is minimum. For Example:

Update Range 1 3 with value 3 
Array  3 3 3 Max.val Max.val
Now update 2 and 4 with 1
Array  3 1 1 1 Max.val
Now update 1 and 2 with 2
Array  2 1 1 1 Max.val
i.e update only if(A[i]>value) A[i]=value;

After the above Query I have to display my final array:i.e 2 1 1 1 Max.val

I am using Segment Tree to solve this Question but I am getting TLE(Time limit Exceeded). I don't know why ? My approach is logN. Here is my update function

public static void lets_change(int curr, int[] T, int x, int y, int a, int b, int c) {
    // x=0 and y=n and a=L , b=R and C= Value to be updated
    // T contains Integer.Max_value at start
    if (x > y || b < x || a > y)
        return;

    if (x == y) {
        T[curr] = Math.min(T[curr], c);
        return;
    }
    lets_change(2 * curr, T, x, (x + y) / 2, a, b, c);
    lets_change(2 * curr + 1, T, (x + y) / 2 + 1, y, a, b, c);

    T[curr] = Math.min(T[2 * curr], T[2 * curr + 1]);
}

Constraints:

N<=10^5;
Q<=10^5;
1<L<=R<=10^5

What have I done wrong or there is any better way? Call a function:

for(int i=0;i<Q;i++){
    int l = in.nextInt()-1;
    int r = in.nextInt()-1;
    int c = in.nextInt();
    lets_change(1,T,0,n-1,l, r,c);
}
OmG
  • 18,337
  • 10
  • 57
  • 90
user4996457
  • 161
  • 1
  • 7

4 Answers4

3

Your approach isn't O(log n), since to update from L to R you have at least to update all positions between L and R (T[curr] = Math.min(T[curr], c)).

To really achieve O(log n) updates, you have to implement a segment tree with lazy propagation. The gist of the structure is to avoid updating every position. Whenever faced with a recursive update that covers the entire range, do not update right away, just mark the range node to be updated later. And when updating (or querying), propagate the scheduled updates whenever needed (when the query or update only covers a part of the range and you need to go deeper).

Juan Lopes
  • 10,143
  • 2
  • 25
  • 44
1

You can do it in O(qlog(n)) where q is the number of queries.

The idea is to use BIT(Binary Indexed Trees)

// C++ program to demonstrate Range Update
// and Range Queries using BIT
#include <iostream>
using namespace std;

// Returns sum of arr[0..index]. This function assumes
// that the array is preprocessed and partial sums of
// array elements are stored in BITree[]
int getSum(int BITree[], int index)
{
    int sum = 0; // Initialize result

    // index in BITree[] is 1 more than the index in arr[]
    index = index + 1;

    // Traverse ancestors of BITree[index]
    while (index>0)
    {
        // Add current element of BITree to sum
        sum += BITree[index];

        // Move index to parent node in getSum View
        index -= index & (-index);
    }
    return sum;
}

// Updates a node in Binary Index Tree (BITree) at given
// index in BITree.  The given value 'val' is added to
// BITree[i] and all of its ancestors in tree.
void updateBIT(int BITree[], int n, int index, int val)
{
    // index in BITree[] is 1 more than the index in arr[]
    index = index + 1;

    // Traverse all ancestors and add 'val'
    while (index <= n)
    {
        // Add 'val' to current node of BI Tree
        BITree[index] += val;

        // Update index to that of parent in update View
        index += index & (-index);
    }
}

// Returns the sum of array from [0, x]
int sum(int x, int BITTree1[], int BITTree2[])
{
    return (getSum(BITTree1, x) * x) - getSum(BITTree2, x);
}


void updateRange(int BITTree1[], int BITTree2[], int n,
                 int val, int l, int r)
{
    // Update Both the Binary Index Trees
    // As discussed in the article

    // Update BIT1
    updateBIT(BITTree1,n,l,val);
    updateBIT(BITTree1,n,r+1,-val);

    // Update BIT2
    updateBIT(BITTree2,n,l,val*(l-1));
    updateBIT(BITTree2,n,r+1,-val*r);
}

int rangeSum(int l, int r, int BITTree1[], int BITTree2[])
{
    // Find sum from [0,r] then subtract sum
    // from [0,l-1] in order to find sum from
    // [l,r]
    return sum(r, BITTree1, BITTree2) -
           sum(l-1, BITTree1, BITTree2);
}


int *constructBITree(int n)
{
    // Create and initialize BITree[] as 0
    int *BITree = new int[n+1];
    for (int i=1; i<=n; i++)
        BITree[i] = 0;

    return BITree;
}

// Driver Program to test above function
int main()
{
    int n = 5;

    // Construct two BIT
    int *BITTree1, *BITTree2;

    // BIT1 to get element at any index
    // in the array
    BITTree1 = constructBITree(n);

    // BIT 2 maintains the extra term
    // which needs to be subtracted
    BITTree2 = constructBITree(n);

    // Add 5 to all the elements from [0,4]
    int l = 0 , r = 4 , val = 5;
    updateRange(BITTree1,BITTree2,n,val,l,r);

    // Add 2 to all the elements from [2,4]
    l = 2 , r = 4 , val = 10;
    updateRange(BITTree1,BITTree2,n,val,l,r);

    // Find sum of all the elements from
    // [1,4]
    l = 1 , r = 4;
    cout << "Sum of elements from [" << l
         << "," << r << "] is ";
    cout << rangeSum(l,r,BITTree1,BITTree2) << "\n";

    return 0;
}

An Efficient Solution is to make sure that both queries can be done in O(Log n) time. We get range sum using prefix sums. How to make sure that update is done in a way so that prefix sum can be done quickly? Consider a situation where prefix sum [0, k] (where 0 <= k < n) is needed after range update on range [l, r]. Three cases arises as k can possibly lie in 3 regions.

Case 1: 0 < k < l The update query won’t affect sum query.

Case 2: l <= k <= r Consider an example:

Add 2 to range [2, 4], the resultant array would be: 0 0 2 2 2 If k = 3 Sum from [0, k] = 4 How to get this result? Simply add the val from lth index to kth index. Sum is incremented by “val*(k) – val*(l-1)” after update query.

Case 3: k > r For this case, we need to add “val” from lth index to rth index. Sum is incremented by “valr – val(l-1)” due to update query.

Observations : Case 1: is simple as sum would remain same as it was before update.

Case 2: Sum was incremented by valk – val(l-1). We can find “val”, it is similar to finding the ith element in range update and point query article. So we maintain one BIT for Range Update and Point Queries, this BIT will be helpful in finding the value at kth index. Now val * k is computed, how to handle extra term val*(l-1)? In order to handle this extra term, we maintain another BIT (BIT2). Update val * (l-1) at lth index, so when getSum query is performed on BIT2 will give result as val*(l-1).

Case 3 : The sum in case 3 was incremented by “val*r – val (l-1)”, the value of this term can be obtained using BIT2. Instead of adding, we subtract “val(l-1) – valr” as we can get this value from BIT2 by adding val(l-1) as we did in case 2 and subtracting val*r in every update operation.

Update Query 
Update(BITree1, l, val)
Update(BITree1, r+1, -val)
UpdateBIT2(BITree2, l, val*(l-1))
UpdateBIT2(BITree2, r+1, -val*r)

Range Sum 
getSum(BITTree1, k) *k) - getSum(BITTree2, k)

Source: geeksforgeeks.org

Asif Ali
  • 11
  • 4
0
void lets_change(int curr, int T[] , int x, int y, int a, int b, int c) {

       if(lazy[curr] != inf ) { // This node needs to be updated
        T[curr] =min(T[curr], lazy[curr]); // Update it

        if(x != y) {
            lazy[curr*2] = min(lazy[2*curr],lazy[curr]); // Mark child as lazy
                lazy[curr*2+1] = min(lazy[2*curr+1],lazy[curr]); // Mark child as lazy
        }

        lazy[curr] = inf; // Reset it
    }

    if (x > y || b < x || a > y)
        return;

    if (x == y) {
        T[curr] = min(T[curr], c);
        return;
    }
    lets_change(2 * curr, T, x, (x + y) / 2, a, b, c);
    lets_change(2 * curr + 1, T, (x + y) / 2 + 1, y, a, b, c);

    T[curr] = min(T[2 * curr], T[2 * curr + 1]);

}

Here is my version of lazy, it's in c++ though. Add the lazy part to your query function and you will be good to go. If you need to access the whole array you may need to redesign your query function .

TT.
  • 15,774
  • 6
  • 47
  • 88
oflocon
  • 11
  • 4
0
//Modifying an element is also quite straightforward and takes time proportional to the height of the tree, which is O(log(n)).** 
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5;
    int t[2*N];
    int n;
    void build()//build the tree 
    {
        for(int i=n-1;i>0;i--)
        {
            t[i]=t[i<<1]+t[i<<1|1];
        }
    }
    void query(int l,int r)
    {
        int res=0;
        for(l+=n,r+=n;l<r;l>>=1,r>>=1)
        {
            if(l&1)res+=t[l++];
            if(r&1)res+=t[--r];
        }
        cout<<res;
    }

    void modify(int p,int value)//updating the value
    {
        for(t[p+=n]=value; p>1 ; p>>=1)
        {
            t[p>>1]=t[p]+t[p^1];
        }
    }

    int main()
    {
        int r1,r2;
        cin>>n;
        for(int i=0;i<n;i++)
        {
           cin>>t[i+n];
        }
        build();
        cin>>r1>>r2
        modify(r1,r2);
        query(0,4);
        }
    }

Now let's see why this works, and works very efficient.

As you could notice from the picture, leaves are stored in continuous nodes with indices starting with n, element with index i corresponds to a node with index i + n. So we can read initial values directly into the tree where they belong.

Before doing any queries we need to build the tree, which is quite straightforward and takes O(n) time. Since parent always has index less than its children, we just process all the internal nodes in decreasing order. In case you're confused by bit operations, the code in build() is equivalent to t[i] = t[2*i] + t[2*i+1].

Modifying an element is also quite straightforward and takes time proportional to the height of the tree, which is O(log(n)). We only need to update values in the parents of given node. So we just go up the tree knowing that parent of node p is p / 2 or p>>1, which means the same. p^1 turns 2 * i into 2 * i + 1 and vice versa, so it represents the second child of p's parent.

General idea is the following. If l, the left interval border, is odd (which is equivalent to l&1) then l is the right child of its parent. Then our interval includes node l but doesn't include it's parent. So we add t[l] and move to the right of l's parent by setting l = (l + 1) / 2. If l is even, it is the left child, and the interval includes its parent as well (unless the right border interferes), so we just move to it by setting l = l / 2. Similar argumentation is applied to the right border. We stop once borders meet.

No recursion and no additional computations like finding the middle of the interval are involved, we just go through all the nodes we need, so this is very efficient.