--- id: PURS title: "Point Update Range Sum" author: Benjamin Qi prerequisites: - Silver - Prefix Sums description: Introducing Binary Indexed Trees & Indexed Sets (C++ only). frequency: 3 --- import { Problem } from "../models"; export const metadata = { problems: { sample: [ new Problem("YS", "Point Add Range Sum", "point_add_range_sum", "Easy", false, ["PURS"], ""), new Problem("CSES", "Range Sum Queries II", "1648", "Easy", false, ["PURS"], "Can also do range XOR queries w/ update."), new Problem("SPOJ", "Inversion Counting", "INVCNT", "Easy", false, ["PURS"]), ], practice: [ new Problem("CSES", "List Removals", "1749", "Easy", false, undefined, "easy with indexed set"), new Problem("CSES", "Salary Queries", "1144", "Easy", false, undefined, "easy with indexed set"), new Problem("CSES", "Range Update Queries", "1651", "Easy", false, undefined, "not harder than point update range query"), new Problem("CSES", "Increasing Subsequence II", "1748", "Easy", false, undefined, ""), new Problem("CSES", "Intersection Points", "1740", "Easy", false, undefined, ""), new Problem("Kattis", "Mega Inversions", "megainversions", "Easy", false, undefined, "also just inversion counting"), new Problem("HackerEarth", "Twin Permutations", "https://www.hackerearth.com/practice/data-structures/advanced-data-structures/fenwick-binary-indexed-trees/practice-problems/algorithm/mancunian-and-twin-permutations-d988930c/description/", "Easy", false, undefined, "Offline 2D queries can be done with a 1D data structure"), new Problem("CSES", "Distinct Values Queries", "1734", "Normal", false, undefined, "Do queries in increasing order of $a$."), new Problem("CSES", "Robot Path", "1742", "Hard", false, undefined, ""), new Problem("Gold", "Haircut", "1041", "Easy", false, undefined, ""), new Problem("Gold", "Balanced Photo", "693", "Easy", false, undefined, ""), new Problem("Gold", "Circle Cross", "719", "Easy", false, undefined, ""), new Problem("Gold", "Sleepy Cow Sort", "898", "Easy", false, undefined, "All USACO problems (aside from some special exceptions) have only one possible output."), new Problem("Plat", "Mincross", "720", "Easy", false, undefined, ""), new Problem("Silver", "Out of Sorts", "834", "Normal", false, undefined, "aka Sorting Steps: https://csacademy.com/contest/round-42/task/sorting-steps/ - Of course, this doesn't require anything other than sorting but fast range sum queries may make this easier."), new Problem("Gold", "Out of Sorts", "837", "Hard", false, undefined, ""), ] } }; ## Sample Problems ## Binary Indexed Tree A *Binary Indexed Tree* allows you to perform the following tasks in $O(\log N)$ time each on an array of size $N$: - Update the element at a single position (point). - Query the sum of a prefix of the array. Aka *Fenwick Tree*. ### Tutorials * CPH 9.2, 9.4 * very good * [CSAcademy BIT](https://csacademy.com/lesson/fenwick_trees) * also very good * [cp-algorithms Fenwick Tree](https://cp-algorithms.com/data_structures/fenwick.html) * extends to range increment and range query, although this is not necessary for gold * [Topcoder BIT](https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/) My implementation can be found [here](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BIT%20(9.2).h), and can compute range sum queries for any number of dimensions. ## Finding K-th Element in BIT? ## Indexed Set In the special case where all elements of the array are either zero or one (which is the case for several gold problems), users of C++ will find [indexed set](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/STL%20(5)/IndexedSet.h) useful. Using this, we can solve "Inversion Counting" in just a few lines (with template). `Tree` behaves mostly the same way as `set` with the additional functions * `order_of_key(x)`: counts the number of elements in the indexed set that are strictly less than `x`. * `find_by_order(k)`: similar to `find`, returns the iterator corresponding to the `k`-th lowest element in the set (0-indexed). See the link for more examples of usage. ```cpp #include using namespace std; #include #include using namespace __gnu_pbds; template using Tree = tree, rb_tree_tag, tree_order_statistics_node_update>; int main() { int T; cin >> T; for (int i = 0; i < T; ++i) { int n; cin >> n; Tree TS; long long numInv = 0; for (int j = 0; j < n; ++j) { int x; cin >> x; numInv += j-TS.order_of_key(x); // gives # elements before it > x TS.insert(x); } cout << numInv << "\n"; } } ``` Note that if it were not the case that all elements of the input array were distinct, then this code would be incorrect since `Tree` would remove duplicates. Instead, we would use an indexed set of pairs (`Tree>`), where the first element of each pair would denote the value while the second would denote the array position. ## Practice Problems - Haircut, Balanced Photo, and Circle Cross are just variations on inversion counting.