This repository has been archived on 2022-06-22. You can view files and clone it, but cannot push or open issues or pull requests.
usaco-guide/content/4_Gold/PURS.mdx
Benjamin Qi 0c1d150c1b ++
2020-07-18 23:32:24 -04:00

218 lines
8.8 KiB
Text

---
id: PURS
title: "Point Update Range Sum"
author: Benjamin Qi
prerequisites:
- prefix-sums
description: "Introducing Segment Trees, Binary Indexed Trees, and Indexed Sets (C++ only)."
frequency: 3
---
import { Problem } from "../models";
export const metadata = {
problems: {
seg: [
new Problem("CSES", "Range Minimum Queries II", "1649", "Intro|Easy", false, ["PURQ"], ""),
new Problem("YS", "Point Set Range Composite", "point_set_range_composite", "Easy", false, ["PURQ"], "Order of operations matters!"),
new Problem("Plat", "Slingshot", "816", "Very Hard", false, ["PURQ"], ""),
],
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("HE", "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, ""),
],
usaco: [
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, ""),
new Problem("Old Gold", "Cow Hopscotch", "532", "Hard", false, [], ""),
new Problem("Plat", "Out of Sorts", "840", "Hard", false, [], ""),
],
}
};
<Problems problems={metadata.problems.sample} />
<br />
Most gold range query problems require you to support 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 some consecutive subarray.
Both **segment trees** and **binary indexed trees** can accomplish this.
## Segment Tree
<Problems problems={metadata.problems.seg} />
A **segment tree** allows you to do point update and range query in $O(\log N)$ time each for **any** associative operation, not just summation.
### Resources
<Info title="Pro Tip">
You can skip more advanced applications such as **lazy propagation** for now. They will be covered in [platinum](../plat/seg-ext).
</Info>
<Resources>
<Resource source="CSA" title="Segment Trees" url="segment_trees" starred>interactive</Resource>
<Resource source="CPH" title="9.3 - Segment Trees" starred>same implementation as below</Resource>
<Resource source="CPC" title="3 - Data Structures" url="03_data_structures" starred>see slides after union-find</Resource>
<Resource source="cp-algo" title="Segment Tree" url="data_structures/segment_tree.html" starred></Resource>
<Resource source="PAPS" title="11.2.3 - Segment Trees"></Resource>
</Resources>
### Implementations
<Resources>
<Resource source="CF" title="AICash - Efficient and easy segment trees" url="blog/entry/18051" starred>simple implementation</Resource>
<Resource source="Benq" title="SegTree" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/SegTree%20(9.2).h">based off above</Resource>
</Resources>
<LanguageSection>
<CPPSection>
```cpp
template<class T> struct Seg { // comb(ID,b) = b
const T ID = 0; T comb(T a, T b) { return a+b; }
int n; vector<T> seg;
void init(int _n) { n = _n; seg.assign(2*n,ID); }
void pull(int p) { seg[p] = comb(seg[2*p],seg[2*p+1]); }
void upd(int p, T val) { // set val at position p
seg[p += n] = val; for (p /= 2; p; p /= 2) pull(p); }
T query(int l, int r) { // sum on interval [l, r]
T ra = ID, rb = ID;
for (l += n, r += n+1; l < r; l /= 2, r /= 2) {
if (l&1) ra = comb(ra,seg[l++]);
if (r&1) rb = comb(seg[--r],rb);
}
return comb(ra,rb);
}
};
```
</CPPSection>
</LanguageSection>
## Binary Indexed Tree
Implementation is shorter than segment tree, but maybe more confusing at first glance.
### Resources
<Resources>
<Resource source="CSA" title="Fenwick Trees" url="fenwick_trees" starred>interactive</Resource>
<Resource source="CPH" title="9.2, 9.4 - Binary Indexed Tree" starred>similar to above</Resource>
<Resource source="cp-algo" title="Fenwick Tree" url="data_structures/fenwick.html">also similar to above</Resource>
<Resource source="TC" title="Binary Indexed Trees" url="binary-indexed-trees"></Resource>
</Resources>
### Implementations
<LanguageSection>
<CPPSection>
<Resources>
<Resource source="CF" title="mouse_wireless - Multi-dimensional BITs with Templates" url="https://codeforces.com/blog/entry/64914" starred></Resource>
<Resource source="Benq" title="BIT" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BIT%20(9.2).h">based off above</Resource>
</Resources>
</CPPSection>
</LanguageSection>
## Finding $k$-th Element
Suppose that we want a data structure that supports all the operations as a `set` in C++ in addition to the following:
- `order_of_key(x)`: counts the number of elements in the 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).
### Indexed Set
Luckily, such a built-in data structure already exists in C++.
<Resources>
<Resource source="CF" url="blog/entry/11080" title="adamant - Policy Based Data Structures" starred> </Resource>
<Resource source="Benq" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/STL%20(5)/IndexedSet.h" title="Indexed Set"></Resource>
</Resources>
Using this, we can solve "Inversion Counting" in just a few lines (with template).
<!-- <Spoiler title="INVCNT with Indexed Set"> -->
```cpp
#include <bits/stdc++.h>
using namespace std;
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
template <class T> using Tree = tree<T, null_type, less<T>,
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<int> 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";
}
}
```
<!-- </Spoiler> -->
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<int>` would remove duplicates. Instead, we would use an indexed set of pairs (`Tree<pair<int,int>>`), where the first element of each pair would denote the value while the second would denote the position of the value in the array.
### With a BIT
Assumes all updates are in the range $[1,N]$.
<Resources>
<Resource source="CF" url="blog/entry/11275" title="adamant - About Ordered Set" starred> log N </Resource>
</Resources>
<!-- <Resource source="GFG" url="order-statistic-tree-using-fenwick-tree-bit" title="Order Statistic Tree using BIT"> log^2 N </Resource> -->
### With a Segment Tree
Covered in [platinum](../plat/seg-ext).
## Practice Problems
<Problems problems={metadata.problems.practice} />
## USACO Problems
Haircut, Balanced Photo, and Circle Cross are just variations on inversion counting.
<Problems problems={metadata.problems.usaco} />