Line tree learning summary Part 3 - line tree + discretization

This paper is about the explanation of line tree + discretization

Forced operation

Recently, I have done some things, although they are all in the original plan, but the reason for the beginning is a little forced to start.

It's a good excuse for me to encourage myself to do things in the name of being forced = w = I'm not a pervert

Catalog

The significance of discretization

One thing that people who know more about him will find is that when he introduces something, he always tries to understand and understand what he thinks is difficult, important or difficult to find on the Internet. Detailed, as clear as possible to reveal, so that they think it can make as few people as possible to take the old detour.

So I want to talk about the significance of discretization and learning discretization.

After doing a lot of competitions, it will be found that line tree as a powerful tool, seems not so easy to use.

Even if you understand the content of the first two articles, you will find that... What's the use of this thing??? It seems that you can't use any other scenarios except line tree template.

In my opinion, this situation is because the line segment tree is based on a data structure and the idea of a tree management interval

Picture source: Baidu Encyclopedia

As shown in the figure, discretization can change the number of trees from management 1 - 1000 to management 1 and management 2. What's the use of discretization

Just like the template question before Enemy troops deployment HDU-1166 Let you manage the number of people in a range. Now I let you manage 233 tents in a range. Isn't it a bit muddled? Let's look at a classic line tree + discretization problem

First of all, let's take a look at this topic: Mayor's posters POJ-2528

It's called "mayor's seals." it's about putting up posters in order, asking you how many different posters people can see in the end

The topic said that the length (data range) of the poster is 1 < = Li < = RI < = 10000000 (1E7). If you want to directly maintain such a large range, the size of the line tree is 4e7, which is about 4e7*log(4e7) ≈ 1e9 (ps: 1e8's time complexity is about 1s). To directly build a tree, it will take 10s, let alone the subsequent operations...

So we need to use discretization: put limited data in the priority space. Because we will find that the maximum number of posters is 10000. We need to consider saving both sides of the posters, which is the data amount of 2e4. Building trees is 8e4, so discretization is a good way to optimize time and space

The method of discretization

The understanding of discretization is just like the example of Baidu Encyclopedia: change 1, 999, 100000, 15 into 1, 3, 4, 2

We need to determine the position of each number in this pile, so we need two steps: sorting and de duplication

Use two functions in the < algorithm > library, sort(), unique(), and upper_bound()

  1. First, enter an n to indicate that there are n posters
  2. Store the data into the original data array orig and the discrete data array bcre. It is strongly recommended that you use the vector container instead of the array. You can also predetermine the size of the array, just like the traditional array, and the operation is more flexible
  3. n*2 is convenient for later use, because we store both sides of the poster, so the elements in the array must be 2n
  4. sort() is used to sort elements within an array interval
  5. unique() is used to delete the same adjacent elements in the array interval, and only one remains. After sorting, deleting like this is de spicy
  6. After finishing the work of de duplication, traverse the original array, and use the upper_bound() function to find out the order of each number in the original data in all numbers
    • upper_bound() returns the position of the first number greater than the specified number in the specified array interval
    • By subtracting the starting position bcre.begin() from this position, we can find out how many numbers are in front including this number

    Since the number in the original data must exist in the data after de duplication, it can also be replaced here

    orig[i] = lower_bound(bcre.begin(), bcre.end(), orig[i]) - bcre.begin() + 1;
    

    lower_bound() returns the position of the first number greater than or equal to the specified number in the specified array interval

//We strongly recommend that you use vector containers instead of arrays
typedef int hip;
vector<hip> orig, bcre;
hip n; cin >> n;
//2. Save data
for (hip i = 1; i <= n; i++) {
	hip a, b; cin >> a >> b;
	orig.push_back(a);
	orig.push_back(b);
	bcre.push_back(a);
	bcre.push_back(b);
} n <<= 1;
//4.5.6. Discretization
sort(bcre.begin(), bcre.end());
unique(bcre.begin(), bcre.end());
for (hip i = 0; i < n; i += 1)
	orig[i] = upper_bound(bcre.begin(), bcre.end(), orig[i]) - bcre.begin();
        //orig[i] = lower_bound(bcre.begin(), bcre.end(), orig[i]) - bcre.begin() + 1;

In this way, the work of discretization is finished and explained clearly.

Because he likes to abuse STL as much as he can, here's a simple way to say another way. In fact, the difference is almost the same, but this sort adds weight... Don't you know that

However, the goose can't use lower bound() or upper bound() directly, so it needs to make a vector container for storage. The worst problem is that poj doesn't support advanced for loops, and it can only be traversed by iterators. In general, the gain is not worth the loss. Let's see the situation

typedef int hip;
vector<hip> orig, acre;
set<hip> bcre;
hip n; cin >> n;
for (hip i = 1; i <= n; i++) {
        hip a, b; cin >> a >> b;
        orig.push_back(a);
        orig.push_back(b);
        bcre.insert(a);
        bcre.insert(b);
} n <<= 1;
for (set<hip>::iterator i = bcre.begin(); i != bcre.end(); i++)
	acre.push_back(*i);
for (hip i = 0; i < n; i += 1)
	orig[i] = upper_bound(bcre.aegin(), acre.end(), orig[i]) - acre.begin();

Operation of segment tree

After the completion of discretization, we need to consider how to use line tree to process these data quickly. If we use array to count tens of thousands of data, it is obviously impractical, so we still need to use line tree to operate

Because what we need here is the number of different posters exposed, so it is not the same as the previous line tree operation:

  • First of all, the boundary of the visit area should be clear, and only==
  • Second, the delivery of tags can only be used when updating, and there is no need for delivery when the last request is made
  • Only need to update a tag. The tag of this point is the value of this point. Because it is a poster, the original value is directly overwritten when updating
  • The initial value is a nonexistent number, which can be 0 for this problem
  • Operation in main function
    1. Input data
    2. Discretization
    3. Build up trees
    4. Update segment tree
    5. Find the number of different values
  • Because the functions are too messy, they are explained in the comments. Please read the comments carefully to understand the code

I found that the previous two pieces of code didn't seem to have been pasted out in the form of structure. Maybe they will be updated in the future. Here's a post for you at the end of the article Basic segment tree template Different for reference

//Custom data type, because sometimes when the program is finished, it needs to use long long, so it is directly defined as a unique data type for my own use
typedef int hip;
//Define the data quantity, 1e4 posters, one poster has two data on the left and right sides, so the original data quantity is 2e4
const hip maxn = hip(2e4 + 9);
//Define all nodes and right child nodes
#define L t << 1
#define R t << 1 | 1
//Define the structure of a node and, by the way, an array tree as a tree
struct P {hip l, r, m, f;}tree[maxn << 2];
//Tree building, all nodes are predefined to 0
void build(hip l, hip r, hip t) {
        tree[t] = {l, r, (l + r) >> 1, 0};
        if (l == r) return ;
        build(l, tree[t].m, L);
        build(tree[t].m + 1, r, R);
}
//If this node has posters but needs to access child nodes, you need to pass down the tags
void push_down(hip t) {
        if (tree[t].f) {
                tree[L].f = tree[t].f;
                tree[R].f = tree[t].f;
                tree[t].f = 0;
        }
}
//Update the middle mark of pl - pr to n, indicating that a new poster has been pasted
void update(hip pl, hip pr, hip n, hip t) {
        //If the interval completely matches, or has traversed to the child node, directly mark the return
        if ((pl == tree[t].l && pr == tree[t].r) || tree[t].l == tree[t].r) {
                tree[t].f = n; return ;
        }
        //If you want to access a child node, because the child node is to update and may only update a part of the child node, you need to pass the tag to the child node first
        push_down(t);
        //If the update interval is completely in the left subinterval, it will traverse to the left subnode
        //If the update interval is completely in the right subinterval, then it will traverse the right subnode
        //If there are both sides, traverse them, and change the interval of update request
        //Since the poster is required to be updated for the specified [every point], the range must be "= ="
        if (pr <= tree[t].m) update(pl, pr, n, L);
        else if (pl > tree[t].m) update(pl, pr, n, R);
        else update(pl, tree[t].m, n, L), update(tree[t].m + 1, pr, n, R);
}
//We need to record the number of [different tags], so use the set set container to directly de duplicate
set<hip> rest;
//Direct access to all tags of used points
void query(hip ql, hip qr, hip t) {
        //If this point has a mark, record the mark directly and return
        if (tree[t].f) {rest.insert(tree[t].f); return ;}
        //If you don't have a tag but are already a child node, you shouldn't continue to traverse down
        if (tree[t].l == tree[t].r) return ;
        //At this time, all subintervals are accessed
        //Because the subintervals on both sides may be updated differently, you should not push down here
        //Also, because of precise access, the access sub section needs to modify the access request section
        query(ql, tree[t].m, L);
        query(tree[t].m + 1, qr, R);
}

int main() {
        ios::sync_with_stdio(0); cin.tie(0);
        hip t; cin >> t; while (t--) {
                vector<hip> orig, bcre;
                hip n; cin >> n;
                //Input data
                for (hip i = 1; i <= n; i++) {
                        hip a, b; cin >> a >> b;
                        orig.push_back(a);
                        orig.push_back(b);
                        bcre.push_back(a);
                        bcre.push_back(b);
                } n <<= 1;
                //Discretization
                sort(bcre.begin(), bcre.end()); unique(bcre.begin(), bcre.end());
                for (hip i = 0; i < n; i += 1)
                        orig[i] = upper_bound(bcre.begin(), bcre.end(), orig[i]) - bcre.begin();
                //Create tree - update line tree
                build(1, n, 1);
                //Here are two visits at a time, which are the left and right intervals of the previously input posters in order, only the left and right intervals after discretization
                for (hip i = 1; i < n; i += 2)
                        update(orig[i - 1], orig[i], i, 1);
                //Empty this collection container before solving
                rest.clear();
                //The data after discretization uses 1 - n all data
                //So access "all points" to find all different points
                query(1, n, 1);
                cout << rest.size() << endl;
        }
        return 0;
}

So far, line tree + discretization is about the same

In fact, discretization is also an independent method, which is not only used in the problems related to line tree, I hope I can learn more in the future to give you a better explanation

Mutual encouragement with you

Reference articles

Line tree special line tree + discretization

Line tree + discretization example

Mayor's posters POJ-2528

Complete code of example

#include <iostream>
using namespace std;
typedef int hip;
#include <set>
#include <algorithm>

#include <vector>
const hip maxn = hip(2e4 + 9);
#define L t << 1
#define R t << 1 | 1

struct P {hip l, r, m, f;}tree[maxn << 2];

void build(hip l, hip r, hip t) {
        tree[t] = {l, r, (l + r) >> 1, 0};
        if (l == r) return ;
        build(l, tree[t].m, L);
        build(tree[t].m + 1, r, R);
}

void push_down(hip t) {
        if (tree[t].f) {
                tree[L].f = tree[t].f;
                tree[R].f = tree[t].f;
                tree[t].f = 0;
        }
}

void update(hip pl, hip pr, hip n, hip t) {
        if ((pl == tree[t].l && pr == tree[t].r) || tree[t].l == tree[t].r) {
                tree[t].f = n; return ;
        }
        push_down(t);
        if (pr <= tree[t].m) update(pl, pr, n, L);
        else if (pl > tree[t].m) update(pl, pr, n, R);
        else update(pl, tree[t].m, n, L), update(tree[t].m + 1, pr, n, R);
}

set<hip> rest;

void query(hip ql, hip qr, hip t) {
        if (tree[t].f) {rest.insert(tree[t].f); return ;}
        if (tree[t].l == tree[t].r) return ;
        query(ql, tree[t].m, L);
        query(tree[t].m + 1, qr, R);
}

int main() {
        ios::sync_with_stdio(0); cin.tie(0);
        hip t; cin >> t; while (t--) {
                vector<hip> orig, bcre;
                hip n; cin >> n;
                for (hip i = 1; i <= n; i++) {
                        hip a, b; cin >> a >> b;
                        orig.push_back(a);
                        orig.push_back(b);
                        bcre.push_back(a);
                        bcre.push_back(b);
                } n <<= 1;
                sort(bcre.begin(), bcre.end()); unique(bcre.begin(), bcre.end());
                for (hip i = 0; i < n; i += 1) orig[i] = upper_bound(bcre.begin(), bcre.end(), orig[i]) - bcre.begin();
                build(1, n, 1);
                for (hip i = 1; i < n; i += 2) update(orig[i - 1], orig[i], i, 1);
                rest.clear();
                query(1, n, 1);
                cout << rest.size() << endl;
        }
        return 0;
}

Basic segment tree template

#include <vector>
const hip maxn = hip(1e5 + 9);
#define L t << 1
#define R t << 1 | 1

struct P {hip l, r, m, w, v, f;}tree[maxn << 2];

vector<hip> orig(maxn);

void push_up(hip t) {tree[t].v = tree[L].v + tree[R].v;}

void push_down(hip t) {
        if (tree[t].f) {
                tree[L].f += tree[t].f;
                tree[R].f += tree[t].f;
                tree[L].v += tree[t].f*tree[L].w;
                tree[R].v += tree[t].f*tree[R].w;
                tree[t].f = 0;
        }
}

void build(hip l, hip r, hip t) {
        tree[t] = {l, r, (l + r) >> 1, r - l + 1, 0, 0};
        if (l == r) {tree[t].v = orig[l]; return ;}
        build(l, tree[t].m, L);
        build(tree[t].m + 1, r, R);
        push_up(t);
}

void update(hip pl, hip pr, hip n, hip t) {
        if (pl <= tree[t].l && pr >= tree[t].r) {
                tree[t].f += n;
                tree[t].v += n*tree[t].w;
                return ;
        }
        push_down(t);
        if (pl <= tree[t].m) update(pl, pr, n, L);
        if (pr > tree[t].m) update(pl, pr, n, R);
        push_up(t);
}

hip query(hip ql, hip qr, hip t) {
        if (ql <= tree[t].l && qr >= tree[t].r) return tree[t].v;
        push_down(t);
        hip s = 0;
        if (ql <= tree[t].m) s += query(ql, qr, L);
        if (qr > tree[t].m) s += query(ql, qr, R);
        return s;
}

Please support his personal blog. There are articles here and there H'on personal station
Thanks for your attention, I will try my best to update the website

48 original articles published, 29 praised, 10000 visitors+
Private letter follow

Tags: REST iOS

Posted on Tue, 10 Mar 2020 03:05:59 -0400 by jasonbullard