UMD CP Club Fall 2023 - Meeting 2
In C++, there are some data structures that are very powerful included in
__gnu_pbds
There are some data structures we sometimes use from this library
ordered_set
that allows querying the kth
largest elementunordered_map
priority_queue
that allows \(O(1)\) mergepersistent trie
that can do some operations like treapAll of these are really powerful data structures
The first data structure we will talk about is the BST
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
We will need to include the header file
you can also use <bits/extc++.h>
to include everything
To initialize the data structure, we will need to do
typedef tree<
int,
null_type,
less<int>,
rb_tree_tag,
tree_order_statistics_node_update>
ordered_set;
We typedef
it because it's too long
The data structure has almost the same usage as std::set
but what we really like about this data structure is to do these
ordered_set st;
st.insert(10);
st.insert(5);
st.insert(8);
cout << *st.find_by_order(1) << '\n'; // find the kth largest element
cout << st.order_of_key(1) << '\n'; // find the number of elements < x
cout << st.order_of_key(9) << '\n';
Example Problem: CSES - Josephus Problem II
We can initialize the hash table by doing
gp_hash_table<key, value> ht;
All usage are the same as unordered_map
You can find more informations in this cf blog
We want a data structure that maintains multiple disjoint sets (or a graph)
As we just talked about, we can use prefix sum to answer range queries
What if now, the elements in the array can change?
You are given an array \(a_1, a_2, \cdots, a_n\). There will be \(q\) queries. There are two types of queries
Directly doing this by bruteforcing, we can do the queries in \(O(1)/O(n)\)
Doing this with prefix sums, we can do the queries in \(O(n)/O(1)\)
How can we make this faster?
We will introduce a data structure that can do the following things:
Both in \(O(\log n)\)
You are given an integer \(x\) (int x / long long x)
How can we find the least significant bit (lowbit) of \(x\)?
For example
\(5 = (101)_2 \implies \text{lowbit}(5) = 1\)
\(10 = (1010)_2 \implies \text{lowbit}(10) = 2\)
In computers, signed integers are stored with two's complement
A \(k\)-bit integer stores as binary form \(b_{k-1}b_{k-2}\ldots b_0\) has value
We can show that \(\text{lowbit}(x) = \texttt{x \& -x}\)
and this can be done in \(O(1)\)
Now, we will define an array \(\texttt{bit[i]} = \text{sum}(i-\text{lowbit}(i) + 1, i)\)
The following code handles the query (the prefix sum part)
int query(int pos) {
int ans = 0;
while (pos < MAXN) {
ans += bit[pos];
pos -= pos & -pos;
}
return ans;
}
The following code handles the update
int update(int pos, int val) {
while (pos < MAXN) {
bit[pos] += val;
pos += pos & -pos;
}
return ans;
}
Both of these operations can be written ~4 lines of code
Let's now use it to solve some problems!
We will do this next week (or some day)