Open In App

Counting segment inversions of an Array with updates

Given an array of small integers arr[] and a 2D array of queries[][], In each query array we have the first element indicating the operation type, and the remaining elements are the arguments.

Examples:



Input: arr[] = {1, 2, 3, 6, 5, 4} , queries={{1, 1, 3}, {1, 2, 5}, {2, 2, 8}, {1, 1, 4}}
Output: 0 1 2
Explanation:

  • In the first query, there are no inversions in segment [1, 3], so the output is 0.
  • In the second query, there is one inversion in segment [2, 5]: (6, 5), so the output is 1.
  • In the third query, the Type of the query is 2 so arr is changed to {1 8 3 6 5 4}.
  • In the final query, there are now two inversions in segment [1, 4]: (8,3), (8, 6), so the output is 2.

Naïve Approach: The basic way to solve the problem is as follows:



For operation Type 1, we can use two nested for loops to check for each pair of elements and count the number of them that form an inversion. Then, for operation Type 2, we simply assign the given value to arr[i].

Time Complexity: O(n2)
Auxiliary Space: O(n)

Efficient Approach: To solve the problem follow the below idea:

We can use a Segment tree to optimize the segment queries, while maintaining a good time complexity in updates.

Steps to solve the problem:

Below is the implementation of the above idea:




// C++ code for the above approach:
#include <iostream>
#include <vector>
 
using namespace std;
 
typedef long long ll;
 
// OBS: 0-based values
constexpr int MAX_V = 40 + 5;
 
struct item {
 
    // Number of inversions in range
    ll inv_cnt;
 
    // freq[n] => number of times n appears
    // in seg
    int freq[MAX_V];
};
 
struct segtree {
 
    // Empty seg
    const item NEUTRAL{
        .inv_cnt = 0,
        .freq = { 0 },
    };
 
    int n;
    vector<int> arr;
    vector<item> seg;
 
    item merge(item const& left, item const& right)
    {
        item parent;
 
        parent.inv_cnt = left.inv_cnt + right.inv_cnt;
 
        // prev[i] => #el <= i on right
        int prev[MAX_V];
        prev[0] = right.freq[0];
 
        for (int i = 0; i < MAX_V; i++) {
            parent.freq[i] = left.freq[i] + right.freq[i];
 
            if (i != 0) {
                // avoids overflow
                parent.inv_cnt
                    += 1LL * left.freq[i] * prev[i - 1];
                prev[i] = prev[i - 1] + right.freq[i];
            }
        }
 
        return parent;
    }
 
    item build(int p, int l, int r)
    {
        if (l == r) {
            seg[p].inv_cnt = 0;
            seg[p].freq[arr[l]] = 1;
            return seg[p];
        }
        int m = (l + r) / 2;
        return seg[p] = merge(build(2 * p, l, m),
                              build(2 * p + 1, m + 1, r));
    }
 
    item build() { return build(1, 0, n - 1); }
 
    segtree(vector<int>& arr_val)
        : n(arr_val.size())
        , arr(arr_val)
    {
        seg.resize(4 * n);
        build();
    }
 
    item set(int i, int v, int p, int l, int r)
    {
        if (i < l or i > r)
            return seg[p];
        if (l == r) {
            seg[p].freq[arr[i]] = 0;
            seg[p].freq[v] = 1;
            arr[i] = v;
            return seg[p];
        }
        int m = (l + r) / 2;
        return seg[p]
               = merge(set(i, v, 2 * p, l, m),
                       set(i, v, 2 * p + 1, m + 1, r));
    }
 
    item set(int i, int v)
    {
        return set(i, v, 1, 0, n - 1);
    }
 
    item query(int lx, int rx, int p, int l, int r)
    {
        if (lx > r or l > rx)
            return NEUTRAL;
        if (lx <= l and r <= rx)
            return seg[p];
        int m = (l + r) / 2;
        return merge(query(lx, rx, 2 * p, l, m),
                     query(lx, rx, 2 * p + 1, m + 1, r));
    }
 
    ll query(int lx, int rx)
    {
        return query(lx, rx, 1, 0, n - 1).inv_cnt;
    }
};
 
// Driver code
int main()
{
 
    // Example inputs
    vector<int> arr = { 1, 2, 3, 6, 5, 4 };
    vector<vector<int> > queries = {
        { 1, 1, 3 }, { 1, 2, 5 }, { 2, 2, 8 }, { 1, 1, 4 }
    };
 
    segtree st(arr);
 
    for (auto query : queries) {
        int const type = query[0];
        if (type == 1) {
 
            // Making Zero based indexing
            query[1]--;
            query[2]--;
            cout << st.query(query[1], query[2]) << endl;
        }
        else if (type == 2) {
 
            // Making Zero based indexing
            query[1]--;
            st.set(query[1], query[2]);
        }
    }
 
    return 0;
}




import java.util.Arrays;
import java.util.List;
 
class Item {
    long invCount;
    int[] freq = new int[46]; // Frequency array for values from 0 to 45
 
    public Item() {
        invCount = 0;
        Arrays.fill(freq, 0);
    }
}
 
class SegmentTree {
    final Item NEUTRAL = new Item();
    int n;
    int[] arr;
    Item[] seg;
 
    public SegmentTree(int[] arr) {
        this.n = arr.length;
        this.arr = Arrays.copyOf(arr, n);
        this.seg = new Item[4 * n];
        build(1, 0, n - 1);
    }
 
    Item merge(Item left, Item right) {
        Item parent = new Item();
        parent.invCount = left.invCount + right.invCount;
        int[] prev = new int[46];
        prev[0] = right.freq[0];
 
        for (int i = 0; i < 46; i++) {
            parent.freq[i] = left.freq[i] + right.freq[i];
            if (i != 0) {
                parent.invCount += 1L * left.freq[i] * prev[i - 1];
                prev[i] = prev[i - 1] + right.freq[i];
            }
        }
        return parent;
    }
 
    Item build(int p, int l, int r) {
        if (l == r) {
            seg[p] = new Item();
            seg[p].invCount = 0;
            seg[p].freq[arr[l]] = 1;
            return seg[p];
        }
        int m = (l + r) / 2;
        return seg[p] = merge(build(2 * p, l, m), build(2 * p + 1, m + 1, r));
    }
 
    Item build() {
        return build(1, 0, n - 1);
    }
 
    Item set(int i, int v, int p, int l, int r) {
        if (i < l || i > r) return seg[p];
        if (l == r) {
            seg[p].freq[arr[i]] = 0;
            seg[p].freq[v] = 1;
            arr[i] = v;
            return seg[p];
        }
        int m = (l + r) / 2;
        return seg[p] = merge(set(i, v, 2 * p, l, m), set(i, v, 2 * p + 1, m + 1, r));
    }
 
    Item set(int i, int v) {
        return set(i, v, 1, 0, n - 1);
    }
 
    Item query(int lx, int rx, int p, int l, int r) {
        if (lx > r || l > rx) return NEUTRAL;
        if (lx <= l && r <= rx) return seg[p];
        int m = (l + r) / 2;
        return merge(query(lx, rx, 2 * p, l, m), query(lx, rx, 2 * p + 1, m + 1, r));
    }
 
    long query(int lx, int rx) {
        return query(lx, rx, 1, 0, n - 1).invCount;
    }
}
 
public class Main {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 6, 5, 4};
        List<int[]> queries = Arrays.asList(
            new int[] {1, 1, 3},
            new int[] {1, 2, 5},
            new int[] {2, 2, 8},
            new int[] {1, 1, 4}
        );
 
        SegmentTree st = new SegmentTree(arr);
 
        for (int[] query : queries) {
            int type = query[0];
            if (type == 1) {
                int l = query[1] - 1;
                int r = query[2] - 1;
                System.out.println(st.query(l, r));
            } else if (type == 2) {
                int i = query[1] - 1;
                st.set(i, query[2]);
            }
        }
    }
}




class SegTree:
    def __init__(self, arrVal):
        self.NEUTRAL = Item(45)
        self.n = len(arrVal)
        self.arr = arrVal
        # Empty seg
        self.seg = [Item(45) for _ in range(4 * self.n)]
        self.build(1, 0, self.n - 1)
 
    def merge(self, left, right):
        parent = Item(len(left.freq))
        #avoids overflow
        parent.invCnt = left.invCnt + right.invCnt
        prev = [0] * len(left.freq)
        prev[0] = right.freq[0]
 
        for i in range(len(left.freq)):
            parent.freq[i] = left.freq[i] + right.freq[i]
 
            if i != 0:
                parent.invCnt += 1 * left.freq[i] * prev[i - 1]
                prev[i] = prev[i - 1] + right.freq[i]
 
        return parent
 
    def build(self, p, l, r):
        if l == r:
            self.seg[p].invCnt = 0
            self.seg[p].freq[self.arr[l]] = 1
            return self.seg[p]
        m = (l + r) // 2
        self.seg[p] = self.merge(self.build(2 * p, l, m), self.build(2 * p + 1, m + 1, r))
        return self.seg[p]
 
    def build_tree(self):
        return self.build(1, 0, self.n - 1)
 
    def set(self, i, v, p, l, r):
        if i < l or i > r:
            return self.seg[p]
        if l == r:
            self.seg[p].freq[self.arr[i]] = 0
            self.seg[p].freq[v] = 1
            self.arr[i] = v
            return self.seg[p]
        m = (l + r) // 2
        self.seg[p] = self.merge(self.set(i, v, 2 * p, l, m), self.set(i, v, 2 * p + 1, m + 1, r))
        return self.seg[p]
 
    def set_value(self, i, v):
        return self.set(i, v, 1, 0, self.n - 1)
 
    def query(self, lx, rx, p, l, r):
        if lx > r or l > rx:
            return self.NEUTRAL
        if lx <= l and r <= rx:
            return self.seg[p]
        m = (l + r) // 2
        return self.merge(self.query(lx, rx, 2 * p, l, m), self.query(lx, rx, 2 * p + 1, m + 1, r))
 
    def query_range(self, lx, rx):
        return self.query(lx, rx, 1, 0, self.n - 1).invCnt
 
 
class Item:
    def __init__(self, maxV):
        self.invCnt = 0
        self.freq = [0] * maxV
 
 
def main():
    arr = [1, 2, 3, 6, 5, 4]
    queries = [
        [1, 1, 3],
        [1, 2, 5],
        [2, 2, 8],
        [1, 1, 4]
    ]
 
    st = SegTree(arr)
 
    for query in queries:
        type = query[0]
        if type == 1:
            l = query[1] - 1  # Making Zero based indexing
            r = query[2] - 1  # Making Zero based indexing
            print(st.query_range(l, r))
        elif type == 2:
            i = query[1] - 1  # Making Zero based indexing
            v = query[2]
            st.set_value(i, v)
#Driver code
 
if __name__ == "__main__":
    main()




using System;
using System.Collections.Generic;
 
class Item
{
    public long invCount;
    public int[] freq = new int[46]; // Frequency array for values from 0 to 45
 
    public Item()
    {
        invCount = 0;
        // Initialize frequency array
        Array.Fill(freq, 0);
    }
}
 
class SegmentTree
{
    private readonly Item NEUTRAL = new Item();
    private int n;
    private int[] arr;
    private Item[] seg;
 
    public SegmentTree(int[] arr)
    {
        n = arr.Length;
        // Create a copy of the input array
        this.arr = (int[])arr.Clone();
        seg = new Item[4 * n]; // Initialize segment tree array
        Build(1, 0, n - 1); // Build the segment tree
    }
 
    private Item Merge(Item left, Item right)
    {
        // Merge two items to produce a parent item
        var parent = new Item();
        parent.invCount = left.invCount + right.invCount;
        int[] prev = new int[46];
        prev[0] = right.freq[0];
 
        // Iterate through frequency array to calculate inversions
        for (int i = 0; i < 46; i++)
        {
            parent.freq[i] = left.freq[i] + right.freq[i];
            if (i != 0)
            {
                parent.invCount += 1L * left.freq[i] * prev[i - 1];
                prev[i] = prev[i - 1] + right.freq[i];
            }
        }
        return parent;
    }
 
    private Item Build(int p, int l, int r)
    {
        if (l == r)
        {
            // Leaf node initialization
            seg[p] = new Item();
            seg[p].invCount = 0;
            seg[p].freq[arr[l]] = 1;
            return seg[p];
        }
        int m = (l + r) / 2;
        // Recursive call to build left and right subtrees
        return seg[p] = Merge(Build(2 * p, l, m), Build(2 * p + 1, m + 1, r));
    }
 
    private Item Set(int i, int v, int p, int l, int r)
    {
        // Update a value at index i with value v
        if (i < l || i > r) return seg[p];
        if (l == r)
        {
            seg[p].freq[arr[i]] = 0;
            seg[p].freq[v] = 1;
            arr[i] = v;
            return seg[p];
        }
        int m = (l + r) / 2;
        // Recursive call to update subtrees
        return seg[p] = Merge(Set(i, v, 2 * p, l, m), Set(i, v, 2 * p + 1, m + 1, r));
    }
 
    public Item Set(int i, int v)
    {
        return Set(i, v, 1, 0, n - 1);
    }
 
    private Item Query(int lx, int rx, int p, int l, int r)
    {
        // Query the segment tree within range lx and rx
        if (lx > r || l > rx) return NEUTRAL;
        if (lx <= l && r <= rx) return seg[p];
        int m = (l + r) / 2;
        // Recursive call to query subtrees
        return Merge(Query(lx, rx, 2 * p, l, m), Query(lx, rx, 2 * p + 1, m + 1, r));
    }
 
    public long Query(int lx, int rx)
    {
        return Query(lx, rx, 1, 0, n - 1).invCount;
    }
}
 
class MainClass
{
    public static void Main(string[] args)
    {
        int[] arr = { 1, 2, 3, 6, 5, 4 };
        List<int[]> queries = new List<int[]>
        {
            new int[] { 1, 1, 3 },
            new int[] { 1, 2, 5 },
            new int[] { 2, 2, 8 },
            new int[] { 1, 1, 4 }
        };
 
        SegmentTree st = new SegmentTree(arr);
 
        foreach (var query in queries)
        {
            int type = query[0];
            if (type == 1)
            {
                int l = query[1] - 1;
                int r = query[2] - 1;
                // Query for inversions within the specified range
                Console.WriteLine(st.Query(l, r));
            }
            else if (type == 2)
            {
                int i = query[1] - 1;
                // Set a new value at the specified index
                st.Set(i, query[2]);
            }
        }
    }
}




class Item {
    constructor(maxV) {
        this.invCnt = 0;
        this.freq = Array(maxV).fill(0);
    }
}
class GFG {
    constructor(arrVal) {
        this.NEUTRAL = new Item(45);
        this.n = arrVal.length;
        this.arr = arrVal.slice(); // Create a copy of the input array
        this.seg = Array(4 * this.n).fill().map(_ => new Item(45)); // Initialize segment tree array
        this.build(1, 0, this.n - 1);
        // Build the segment tree
    }
    merge(left, right) {
        const parent = new Item(left.freq.length);
        parent.invCnt = left.invCnt + right.invCnt;
        const prev = Array(left.freq.length).fill(0);
        prev[0] = right.freq[0];
        for (let i = 0; i < left.freq.length; i++) {
            parent.freq[i] = left.freq[i] + right.freq[i];
            if (i !== 0) {
                parent.invCnt += 1 * left.freq[i] * prev[i - 1];
                prev[i] = prev[i - 1] + right.freq[i];
            }
        }
        return parent;
    }
    build(p, l, r) {
        if (l === r) {
            // Leaf node initialization
            this.seg[p].invCnt = 0;
            this.seg[p].freq[this.arr[l]] = 1;
            return this.seg[p];
        }
        const m = Math.floor((l + r) / 2);
        // Recursive call to build left and right subtrees
        this.seg[p] = this.merge(this.build(2 * p, l, m), this.build(2 * p + 1, m + 1, r));
        return this.seg[p];
    }
    buildTree() {
        return this.build(1, 0, this.n - 1);
    }
    set(i, v, p, l, r) {
        if (i < l || i > r) return this.seg[p];
        if (l === r) {
            this.seg[p].freq[this.arr[i]] = 0;
            this.seg[p].freq[v] = 1;
            this.arr[i] = v;
            return this.seg[p];
        }
        const m = Math.floor((l + r) / 2);
        // Recursive call to update subtrees
        this.seg[p] = this.merge(this.set(i, v, 2 * p, l, m), this.set(i, v, 2 * p + 1, m + 1, r));
        return this.seg[p];
    }
    setValue(i, v) {
        return this.set(i, v, 1, 0, this.n - 1);
    }
    query(lx, rx, p, l, r) {
        if (lx > r || l > rx) return this.NEUTRAL;
        if (lx <= l && r <= rx) return this.seg[p];
        const m = Math.floor((l + r) / 2);
        // Recursive call to query subtrees
        return this.merge(this.query(lx, rx, 2 * p, l, m), this.query(lx, rx, 2 * p + 1, m + 1, r));
    }
    queryRange(lx, rx) {
        return this.query(lx, rx, 1, 0, this.n - 1).invCnt;
    }
}
// Driver code
const arr = [1, 2, 3, 6, 5, 4];
const queries = [
    [1, 1, 3],
    [1, 2, 5],
    [2, 2, 8],
    [1, 1, 4]
];
const st = new GFG(arr);
for (const query of queries) {
    const type = query[0];
    if (type === 1) {
        const l = query[1] - 1;
        // Making Zero based indexing
        const r = query[2] - 1;
        // Making Zero based indexing
        console.log(st.queryRange(l, r));
    } else if (type === 2) {
        const i = query[1] - 1;
        // Making Zero based indexing
        const v = query[2];
        st.setValue(i, v);
    }
}

Output
0
1
2











Time Complexity: O(n + q*logn)
Auxiliary Space: O(n)


Article Tags :