Tree array to solve the number of pairs in reverse order (including introduction to tree array)

Keywords: C++ Algorithm


Before learning the tree array to solve the reverse pair. First, let's briefly learn about tree arrays. First, let's explore four problems.
1. What is a tree array?
See the name and meaning, is to use arrays to simulate tree structures. Then another question arises, why not make achievements directly? The answer to this question is not necessary. Tree array can solve the problem. There is no need to build a tree.
2. What problem is it used to solve?
Like the segment tree, it is used to solve the operations such as updating and summation on the interval.
3. What is the difference between line segment tree and line segment tree
In fact, the problems that tree array can solve can be solved by line segment tree, but the coefficients of tree array are much less.
4. Advantages and disadvantages
The advantages are: the time complexity of modification and query is O(logN), the coefficients are much less than the line segment tree, and the code is short (mainly because of lowbit Technology).
The disadvantage is that the function is limited and the complex interval problem cannot be solved.

The following figure is the tree structure of the tree array. Obviously, it has much less coefficients than the line segment tree.

Black represents the tree array (replaced by c[i] later) and red represents the original array (replaced by a[i] later).

1, Create a tree array

1. Single point update, interval query

So how do you get the relationship between nodes in a tree array?
c[1] = a[1].
c[2] = a[1] + a[2].
c[3] = a[3].
c[4] = a[4] + c[3] + c[2] = a[1] + a[2] + a[3] + a[4].
c[5] = a[5].
c[6] = a[5] + a[6].
c[7] = a[7].
c[8] = c[4] + c[6] + c[7] + c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8].

We convert the node value of the previous figure into binary

Now we can find the law of this tree:
c[i] = a[i - 2k + 1] + a[i - 2k + 2] + ...... + a[i].
k here means the number of binary numbers of i from the lowest bit of to 0 when the first 1 appears.

So what should we do to achieve summation?
Take chestnuts for example: if we want to find the sum of the first seven items, SUM7 = c[7] + c[6] + c[4].
According to the K law obtained above, it can be seen that c[6] = c[7 - 2k1], c[4] = c[6 - 2k2].
Then we get such a law: SUMi = c[i] + c[i - 2k1] + c[i - 2k1 - 2k2] +.
Now we have the sum formula, but we have a new problem, 2k how do we find it?
First of all, we must think of finding the k value of the current i value every time, but this is obviously inefficient, so we need to find another way.
Predecessors have found a very simple way to get this k value, which is the lowbit technology I mentioned earlier.

inline int Lowbit(int x)
{
    return x & (-x);
}

Yes, that's right. Just one line can solve this problem.
You can verify the correctness by yourself.

Next, let's see why we can get 2K = I & (- I).
Here, the storage characteristics of negative numbers are used. Negative numbers are stored in the form of complement. There are four cases for X & (- x):

  1. When x equals 0, i.e. 0 & 0, the result is 0.
  2. When x is an odd number, the last bit of the binary number must be 1. If you take the inverse plus 1, there must be no carry. At this time, X and - X are opposite except the last bit, and the result X & (- x) is 1.
  3. When x is even but not to the nth power of 2. Each x can be written as x = y * (2k). In fact, it is obtained by shifting an odd number to the left by K bits. At this time, the binary number of X must have K zeros on the far right, and the k + 1 bit from right to left must be 1. Then, when we reverse X by bit and add 1, the rightmost k bit becomes 1 because of the carry, and the k + 1 bit becomes 0 because of the previous carry. Because there is no carry, the number on the left is just opposite to the number on the corresponding bit of X. At this time, if you press bit and, you get that the k + 1 bit is 1 and the others are all 0. Exactly 2K.
  4. When x is even and to the nth power of 2. Then, only one bit of the binary number of X is 1 (the n + 1 bit from right to left), and there are m zeros on the right. After the bit is reversed and 1 is added, there are m zeros from right to left, and the rest are 1. Then x & (- x) = 2K.

To sum up: X & (- x), when x is 0, the result is 0; When x is an odd number, the result is 1; When x is an even number, the result is the factor of the largest power of 2 in X.

In this way, we can complete the interval query. Next, let's look at the single point update.
As mentioned above, c[i] = a[i - 2k + 1] + a[i - 2k + 2] +... + a[i], if the value of a[i] is updated, all locations containing a[i] will be affected. Similarly, we can get that a[i] is included in c[i + 2k], C [(I + 2K) + 2K].
Here is the code:

inline int Lowbit(int x)
{
    return x & (-x);
}

inline void Add(int i,int k)     //Modify the value of i point
{
    while(i <= n)
    {
        c[i] += k;
        i += Lowbit(i);
    }
}

inline int Query(int i)    //Value of query i
{
    long long res = 0;

    while(i > 0)
    {
        res += c[i];
        i -= Lowbit(i);
    }

    return res;
}

2. Interval update, single point query

When the topic requires the interval to update and query the value of a single point, if the above tree array is also used, each value in the required interval must be updated, so the time complexity is certainly not good.
In this case, we can't use values to build trees. We use the difference method to build trees.

We specify a[0] = 0;
You can get a[i]= Σ ij=1d[j] (d[j] = a[j] - a[j - 1]), which means the sum of the differences of the I items before the value of a[i]. For example, the following array:
a[] = 1 2 3 6 8 9
d[] = 1 1 1 3 2 1
If we add 2 to the interval [2,5], it becomes as follows:
a[] = 1 4 5 8 10 9
d[] = 1 3 1 3 2 -1
We can find that when the value of an interval [x,y] changes, only the values of d[x] and d[y + 1] change, and d[x] is the same operation and d[y + 1] is the opposite operation.
We use this feature to create a tree array of d [] arrays.

#include <iostream>

using namespace std;

int n, m;
int a[50010] = { 0 }, c[50010];    //A original array and c tree array. Remember that the original array a[0] = 0

int Lowbit(int x)
{
    return x & (-x);
}

void Update(int i, int k)   //Add k to position i
{
    while(i <= n)
    {
        c[i] += k;
        i += Lowbit(i);
    }
}

int Query(int i)     //Find the sum of d[1] to d[i], that is, the value of a[i]
{
    int res = 0;

    while(i > 0)
    {
        res += c[i];
        i -= Lowbit(i);
    }

    return res;
}

int main()
{
    cin >> n >> m;

    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&a[i]);

        Update(i, a[i] - a[i - 1]);     //Entering the initial value is also equivalent to updating the tree array
    }

    while(m--)
    {
        int choose;

        scanf("%d",&choose);

        if(choose == 1)     //Modify the value of the x to y interval
        {
            int x, y, k;

            scanf("%d%d%d",&x,&y,&k);

            Update(x, k);          //d[x] is a[x] - a[x - 1] plus k
            Update(y + 1, -k);     //d[y + 1] is a[y + 1] - a[y] minus k
        }
        if(choose == 2)     //Value of query i
        {
            int i;

            scanf("%d",&i);

            printf("%d\n",Query(i));
        }
    }
}

In this way, we change the original operation of updating an interval into the operation of updating two points.

3. Interval update and interval query

The tree like array of difference values described above can get the value of a point. If you need both interval update and interval query. Can we still use difference?
From the previous knowledge, we can get:
Σni=1a[i] = Σni=1 Σij=1d[j];
That is, a[1] + a[2] +... + a[n]

= (d[1]) + (d[1] + d[2]) + ...... + (d[1] + d[2] +...... + d[n])

= n * d[1] + (n - 1) * d[2] + ...... + d[n]

= n * (d[1] + d[2] + ...... + d[n]) - (0 * d[1] + 1 * d[2] + ...... + (n - 1) * d[n])

Then the above formula becomes Σ ni=1a[i] = n * Σ ni=1d[i] - Σ ni=1(d[i] * (i - 1)).

From this formula, we need to maintain two tree arrays,
That is sum1[i] = d[i], sum2[i] = d[i] * (i - 1).

#include <iostream>

using namespace std;

int n, m;
int a[50010] = { 0 };
int sum1[50010];        //Maintenance (d[1] + d[2] +... + d[n])
int sum2[50010];        //Maintenance (0 * d[1] + 1 * d[2] +... + (n - 1) * d[n])

int Lowbit(int x)
{
    return x & (-x);
}

void Update(int i, int k)
{
    int x = i;     //The initial value of i should not be changed. Save it with x

    while(i <= n)
    {
        sum1[i] += k;
        sum2[i] += k * (x - 1);
        i += Lowbit(i);
    }
}

int Query(int i)
{
    int res = 0, x = i;

    while(i > 0)
    {
        res += x * sum1[i] - sum2[i];
        i -= Lowbit(i);
    }

    return res;
}

int main()
{
    cin >> n >> m;

    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&a[i]);

        Update(i, a[i] - a[i - 1]);
    }

    while(m--)
    {
        int choose;

        scanf("%d",&choose);

        if(choose == 1)
        {
            int x, y, k;

            scanf("%d%d%d",&x,&y,&k);

            Update(x, k);
            Update(y + 1, -k);
        }
        if(choose == 2)
        {
            int x, y;

            scanf("%d%d",&x,&y);

            int sum = Query(y) - Query(x - 1);  //In the process of Query(y), the value before x is also added, so it needs to be subtracted

            printf("%d\n",sum);
        }
    }
}

2, Tree array to solve the number of pairs in reverse order

1. What is the reverse order pair?

Let A be an ordered set of N numbers (n > 1), where all numbers are different.
If there is A positive integer i, j such that 1 ≤ I < J ≤ n and A [i] > A [J], then < A [i], A [J] > the ordered symmetry is an inverse pair of A, also known as the inverse number.

That is to say, if there is such a string of numbers: 5 3 6 2 9
Where 5 and 3, 6 and 2, 5 and 2, 3 and 2 are reverse pairs.

2. How to use tree array?

To answer this question, we must first clear the meaning of tree array in this question.
We require that the total number of reverse pairs can be decomposed into the numbers in the array, put them one by one, get the number of reverse pairs at that time, and then add them.
In other words, every time we add a number, we have to get the number of numbers larger than this number in the array at this time
.
Then, for each number added, we record the number smaller than it once, so that when we encounter this number, we can know how many numbers are larger than it. Then our tree array is useful.
Yes, the meaning of tree array here is this number. There are several numbers larger than it before the position of the array.

It should be noted that when we use the tree array to solve the number of pairs in reverse order, we often encounter the situation that the quantity value is too large. At this time, we need to discretize and build the tree array.

In this way, we can find the code to solve the number of pairs in reverse order in the tree array:

#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int MAX = 5e5 +10;

int n;
int a[MAX],b[MAX],c[MAX];

bool cmp (int x, int y)
{
    return a[x] < a[y];
}

inline int Lowbit(int x)
{
    return x & (-x);
}

inline void Add(int pos)
{
    while(pos <= n)
    {
        c[pos]++;
        pos += Lowbit(pos);
    }
}

inline int Query(int pos)
{
    long long res = 0;

    while(pos > 0)
    {
        res += c[pos];
        pos -= Lowbit(pos);
    }

    return res;
}

int main()
{
    cin >> n;

    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&a[i]);

        b[i] = i;          //Initialize b array
    }

    stable_sort(b + 1, b + n + 1, cmp);    //Sort array b in the order of array a from large to small

    long long sum = 0;

    for(int i = 1; i <= n; i++)
    {
        a[b[i]] = i;          //Discretize the a array with the previous sorting function
    }

    for(int i = n; i >= 1; i--)
    {
        sum += Query(a[i] - 1);        //From the back to the front is to find the number of smaller numbers in the array at this time
 
        Add(a[i]);      //Update tree array
    }
    cout << sum;
}

Posted by trassalg on Mon, 29 Nov 2021 22:04:12 -0800