Normal Balance Tree of Luogu P3369 [Template]
Description
- You need to write a data structure (referable title) to maintain some numbers, which need to provide the following operations:
- Insert x number
- Delete the number x (if there are more than one identical number, because only one is deleted)
- Query x ranking (ranking defined as the number of smaller than the current number + 1). If there are multiple identical numbers, because of the ranking of the smallest output)
- Number of queries ranked x
- The precursor of finding x (the precursor is defined as less than X and the largest number)
- Find the succession of x (succession is defined as the number greater than x and the smallest)
Input
- The first action n represents the number of operations. The next n rows have two numbers opt and x, and opt represents the ordinal number of operations (1 < opt < 6).
Output
- For operations 3, 4, 5, 6, output a number per line to indicate the corresponding answer
Sample Input
10 1 106465 4 1 1 317721 1 460929 1 644985 1 84185 1 89851 6 81968 1 492737 5 493598
Sample Output
106465 84185 492737
Data Size
-
Space-time constraints: 1000ms,128M
1.n Data Range: n < 100000
2. Data range of each number: [10 ^ 7 ^, 10 ^ 7 ^]
Solutions:
- Today I was forced to learn Treap, which I had been dragging for half a year. This algorithm has a certain understanding. So I will write a very detailed commentary on this blog.
- But be sure to understand the general principles of Treap before you look at it, such as why you set random weights for each point, the left-to-right principle.
#include <iostream> #include <cstdio> #include <cstdlib> #define N 100005 #define inf 0x7fffffff using namespace std; struct T {int l, r, val, dat, cnt, size;} t[N]; /* * l Left son, r ight son * val It's the key code. dat is a random weight. * cnt Is the number of copies, size is the subtree size * Whether found that this code did not record the father, this is the essence! Make a point's son node update automatically by using references (arguments)! Simplify the code! */ int n, tot, root; int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();} return x *= f; } int New(int val) //New function of a point { t[++tot].val = val, t[tot].dat = rand(); //First assign the node, then give a random number. t[tot].cnt = t[tot].size = 1; //The number of replicas and subtrees is initially 1 (on their own) return tot; } void up(int p) { //Update the function of size. pushup operation similar to segment tree t[p].size = t[t[p].l].size + t[t[p].r].size + t[p].cnt; //Subtree size = left son subtree size + right son subtree size + own copy number } void zig(int &y) //Dextral function { int x = t[y].l; t[y].l = t[x].r, t[x].r = y, y = x; up(t[y].r), up(y); } void zag(int &x) //Left-handed function { int y = t[x].r; t[x].r = t[y].l, t[y].l = x, x = y; up(t[x].l), up(x); } void insert(int &p, int val) { if(!p) {p = New(val); return;} //No more recursion, just build a new node if(val == t[p].val) {t[p].cnt++, up(p); return;} //If this val has been added before, then the number of replicas + if(val < t[p].val) //If you can't find it, go on recursively { insert(t[p].l, val); if(t[t[p].l].dat > t[p].dat) zig(p); //In order to maintain the nature of the big root heap at the same time, if there is a situation that does not satisfy the big root heap, amend it. } else //If you can't find it, go on recursively { insert(t[p].r, val); if(t[t[p].r].dat > t[p].dat) zag(p); //In order to maintain the nature of the big root heap at the same time, if there is a situation that does not satisfy the big root heap, amend it. } up(p); //Because new points are inserted, the size of each point will change, so the size information will be updated. } void erase(int &p, int val) //Delete function with val ue node from subtree p { //The idea of deletion is to transfer the val ue under the node to the leaf node, and then delete it. if(!p) return; if(val == t[p].val) //Find the node where val is located { if(t[p].cnt > 1) {t[p].cnt--, up(p); return;} //If there are multiple copies, directly reduce the number of copies if(t[p].l || t[p].r) //If you have a son, you spin down. { if(!t[p].r || t[t[p].l].dat > t[t[p].r].dat) zig(p), erase(t[p].r, val); /* * !t[p].r That means there is no right subtree, so right-handed is enough. * t[t[p].l].dat > t[t[p].r].dat Because we want to delete the current point, regardless of whether it satisfies the nature of the heap or not with other points (which is deleted anyway), we only consider the heap nature between the two sons and put the random weight on it. */ else zag(p), erase(t[p].l, val); up(p); } else p = 0; //No son says it's already a leaf node, just delete it. return; } val < t[p].val ? erase(t[p].l, val) : erase(t[p].r, val); up(p); } int val_rank(int p, int val) //Query val is the number of functions { if(!p) return 0; //val not found, return if(val == t[p].val) return t[t[p].l].size + 1; //If we find the location of val, then the ranking of Val is the number of sub-trees of the left son + 1. Because the left margin of the point is smaller than the value of the point (query value). if(val < t[p].val) return val_rank(t[p].l, val); //Find that the query point is on the left of the current point and recursively query to the left. return t[t[p].l].size + t[p].cnt + val_rank(t[p].r, val); //It is found that the query point is on the right side of the current point, and because the value of the current point is smaller than the query value, t[p].cnt is added. } int rank_val(int p, int rank) { if(!p) return inf; //I can't find the instructions. It's inf. if(t[t[p].l].size >= rank) return rank_val(t[p].l, rank); //It was found that the query ranked on the left son's side (left interval) if(t[t[p].l].size + t[p].cnt >= rank) return t[p].val; //The previous step excludes the case of the left interval. If rank is in the left and middle (the current node), the value of the current node (the middle interval) is returned directly. return rank_val(t[p].r, rank - t[t[p].l].size - t[p].cnt); //Discovered in the right interval } int nextPre(int val, int tag) //Find the Precursor/Succession: 0 is the Precursor and 1 is the Succession (Personal Habits Write Together) { /* * Take looking for successors as an example. First, retrieve val * After retrieval, there are three situations: 1. No val was found: at this point in the node that has passed, ans is the requirement 2. val was found, but no right subtree was found: ibid., ans is the requirement 3. We find Val and have a right subtree: Start from the right subtree and go left all the time, which is the successor of val. */ int ans = tag == 0 ? 2 : 1, p = root; //Initialize ans, if the search precursor is=-inf, then inf while(p) { if(val == t[p].val) //Successful search { if(!tag && t[p].l) //Left subtree { p = t[p].l; while(t[p].r) p = t[p].r; ans = p; break; } else if(!tag) break; //No left subtree if(tag && t[p].r) //There are right subtrees { p = t[p].r; while(t[p].l) p = t[p].l; ans = p; break; } else if(tag) break; //No right subtree } if(!tag && t[p].val < val && t[p].val > t[ans].val) ans = p; //Trying to update the precursor if(tag && t[p].val > val && t[p].val < t[ans].val) ans = p; //Trying to update successors p = val < t[p].val ? t[p].l : t[p].r; } return t[ans].val; } int main() { New(inf), New(-inf); //Two Sentinel Nodes to Prevent Cross-border Problems t[1].l = 2, root = 1, up(1); //Some initialization cin >> n; for(int i = 1; i <= n; i++) { int op = read(), x = read(); if(op == 1) insert(root, x); else if(op == 2) erase(root, x); else if(op == 3) printf("%d\n", val_rank(root, x) - 1); //Because of the insertion of sentinel nodes, the original first place became the second place. else if(op == 4) printf("%d\n", rank_val(root, x + 1)); //Because of the insertion of sentinel nodes, the first place to be looked up becomes the second (the first name is - inf). else if(op == 5) printf("%d\n", nextPre(x, 0)); else if(op == 6) printf("%d\n", nextPre(x, 1)); } return 0; }