Introduction to Segment Trees (Range Minimum Query)


Range Minimum Query

Given an array A of length N , answer getMin(i, j) qeuries. getMin(i, j) should return the index m , where A[m] = min_{i<k<j}\{A[k]\} .

There are many ways to do this. In the case where A 's element values are allowed to change, and yet we have to maintain getMin(i, j) to work correctly, a segment tree is our best option. Follows is a brief explanation of segment trees for this specific example. I'll put a more generalized explanation in another post.

Segment Trees

The idea behind a segment tree is to build a binary tree where each node represents a segment of A . For Range Minimum Query, each tree node will contain the value of getMin(segment) (the index of the minimum value in the node’s segment of the array).

Now, how are nodes and segments assigned? For simplicity, let’s assume that A ‘s length is a power of two:

  • Starting at the lowest level of the tree, we’ll have N leaf nodes each representing an element of A. As shown in the following figure, if we label leaf nodes from 0 to N - 1 from left to right, each node i will contain the value i .
  • For each internal node node starting from the bottom, we compute node.value as follows: node.value = m, A[m] = min\{A[node.left.value], A[node.right.value]\} . Meaning, a parent node node will get the value of one of its children such that A[node.value] is minimum.
  • Since this is a binary tree with the last level having N nodes, the tree will have the height of log(N) + 1 . Level 0 will have one node, the root, which holds getMin(0, N - 1) . This means that each node on a level L will represent a segment of length 2^{log(N) + 1 - L} .

rmq

The implementation is going to be straight forward from here on. We’ll represent the tree as an array tree of length 2 << ceil(log2(N + 1)). Node i has a left child 2 * i and a right child 2 * i + 1. On an update(i), we’ll traverse the tree from top to bottom, change array value at when we reach the leaf i, and update the tree as we go back up. On a getMin(range), we’ll recurse from root until we visit all maximal sub-segments of the range and return the required value. Here’s an implementation in C++ with this tutorial as a reference:

#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;

class segTree {
    // O(n)
    int *array, *tree;
    int arrayLen, treeLen;

    // O(n)
    void initialize(int node, int b, int e) {
        if (b == e)
            tree[node] = b;
        else {
            // recurse
            initialize(2 * node, b, (b + e) / 2);
            initialize(2 * node + 1, (b + e) / 2 + 1, e);
            // update value
            if (array[tree[2 * node]] <= array[tree[2 * node + 1]])
                tree[node] = tree[2 * node];
            else
                tree[node] = tree[2 * node + 1];
        }
    }
public:
    segTree(int *array, int arrayLen) {
        this->arrayLen = arrayLen;
        this->array = array;
        this->treeLen = 2 << (int)ceil(log2(arrayLen));
        cout << "treeLen=" << treeLen << endl;
        this->tree = new int[treeLen];
        memset(tree, -1, sizeof(int) * treeLen);
        initialize(1, 0, arrayLen - 1);
    }

    // O(log n)
    void update(int i, int v, int node = 1, int b = 0, int e = 0) {
        e = arrayLen - 1 - e;
        if (b == e) {
            array[i] = v;
        } else {
            int mid = (b + e) / 2;
            if (i <= mid)
                update(i, v, 2 * node, b, arrayLen - 1 - mid);
            else
                update(i, v, 2 * node + 1, mid + 1, arrayLen - 1 - e);
            if (array[tree[2 * node]] <= array[tree[2 * node + 1]])
                tree[node] = tree[2 * node];
            else
                tree[node] = tree[2 * node + 1];
        }
    }

    // O(log n)
    int query(int i, int j, int node = 1, int b = 0, int e = 0) {
        e = arrayLen - 1 - e;
        // bad interval
        if (i > e || j < b)
            return -1;
        // good interval
        if (b >= i && e <= j)
            return tree[node];
        // partial interval
        int left = query(i, j, 2 * node, b, arrayLen - 1 - (b + e) / 2);
        int right = query(i, j, 2 * node + 1, (b + e) / 2 + 1, arrayLen - 1 - e);
        if (left == -1)
            return tree[node] = right;
        if (right == -1)
            return tree[node] = left;
        if (array[left] <= array[right])
            return tree[node] = left;
        return tree[node] = right;
    }
};

int main() {
    int A[10] = { 2, 4, 3, 1, 6, 7, 8, 9, 1, 7 };
    segTree t(A, 10);
    cout << "getMin(0, 4) = " << t.query(0, 4) << endl;
    t.update(1, 0);
    cout << "getMin(0, 4) = " << t.query(0, 4) << endl;
    t.update(0, -1);
    cout << "getMin(0, 4) = " << t.query(0, 4) << endl;
    return 0;
}

10263 – Railway


A geometry distance computation problem.
You’re given N broken lines and a point M. Find the point S on the broken line that’s nearest to M.
The required point can be either on one end of a broken line segment, or it can be M’s projection on that segment. The rest is mere find-minimum computation. C++ implementation:

#include <iostream>
#include <complex>
#include <limits>
using namespace std;

typedef long double gtype;
#define inRange(a, b, p, e) ( (p) >= min(a, b) - (e) && (p) <= max(a, b) + (e))
const gtype len_before_point_digits = 1E8;
const gtype epsLen = numeric_limits<gtype>::epsilon() * len_before_point_digits;
typedef complex<gtype> point;
#define x real()
#define y imag()
#define crs(a, b) ( (conj(a) * (b)).y )
#define intrN(a, b, p, q) ( crs((p)-(a),(b)-(a)) * (q) - crs((q)-(a),(b)-(a)) * (p) )
#define intrD(a, b, p, q) ( crs((p)-(a),(b)-(a)) - crs((q)-(a),(b)-(a)) )
#define pointDist(a, b, p) ( crs((b)-(a), (p)-(a)) / abs((b)-(a)) )
#define perp(p) ( point(-(p).y, (p).x) )
#define inRect(a, b, p, e) ( inRange((a).x, (b).x, (p).x, e) && inRange((a).y, (b).y, (p).y, e) )

void min(point* station, point p0, point p1, point m, gtype* minD) {
	// end points
	gtype d = abs(p1 - m);
	if (d <= *minD) {
		*minD = d;
		*station = p1;
	}
	// perp distance
	d = abs(pointDist(p0, p1, m));
	point v = perp(p1 - p0) + m;
	point r = intrN(p0, p1, m, v) / intrD(p0, p1, m, v);
	if (d <= *minD && inRect(p0, p1, r, epsLen)) {
		*minD = d;
		*station = r;
	}
}

int main() {
	cout.precision(4);
	int n;
	point m, start, p[2], station;
	while (cin >> m.x >> m.y) {
		gtype minD = numeric_limits<gtype>::max(), d;
		cin >> n;
		cin >> p[0].x >> p[0].y;
		start = point(p[0]);
		// start point
		d = abs(p[0] - m);
		if (d <= minD) {
			minD = d;
			station = p[0];
		}
		// broken line
		for (int i = 0; i < n; ++i) {
			cin >> p[1].x >> p[1].y;
			min(&station, p[0], p[1], m, &minD);
			p[0] = point(p[1]);
		}
		// end point
		min(&station, p[0], start, m, &minD);
		cout << fixed << station.x << endl << station.y << endl;
	}
	return 0;
}

866 – Intersecting Line Segments


A computational geometry problem in which you’re required determine the number of line segments resulting from the evaluation of all the possible intersections between all given line segments.
Actually, the problem you have to solve is a simplified version so you don’t have to worry about special cases.
Caution!

  • Print a new line BETWEEN each consecutive cases.

C++ implementation using my geometry tools:

#include <algorithm>
#include <iostream>
#include <complex>
#include <cstdlib>
#include <limits>
using namespace std;

typedef long double gtype;
#define equ(a, b, e) ( abs((a) - (b)) <= (e) )
// const
const gtype len_before_point_digits = 1E10;
const gtype epsLen = numeric_limits<gtype>::epsilon() * len_before_point_digits;
// point
typedef complex<gtype> point;
#define x real()
#define y imag()
// vector
#define dot(a, b) ( (conj(a) * (b)).x )
#define crs(a, b) ( (conj(a) * (b)).y )
// line
#define intrN(a, b, p, q) ( crs((p)-(a),(b)-(a)) * (q) - crs((q)-(a),(b)-(a)) * (p) )
#define intrD(a, b, p, q) ( crs((p)-(a),(b)-(a)) - crs((q)-(a),(b)-(a)) )
#define onLin(a, b, p) ( abs(crs((b) - (a), (p) - (a))) <= epsLen )
#define onRay(a, b, p) ( onLin(a, b, p) && dot((b) - (a), (p) - (a)) >= -epsLen )
#define onSeg(a, b, p) ( onRay(b, a, p) && dot((b) - (a), (p) - (a)) >= -epsLen )

int nCase, nSeg, nSegAfter;
point s[128][2];

int main() {
	cin >> nCase;
	for (int caseI = 0; caseI < nCase; ++caseI) {
		cin >> nSeg;
		// input
		for (int i = 0; i < nSeg; ++i)
			cin >> s[i][0].x >> s[i][0].y >> s[i][1].x >> s[i][1].y;
		// solve
		nSegAfter = nSeg;
		for (int i = 0; i < nSeg; ++i) {
			for (int j = i + 1; j < nSeg; ++j) {
				bool intersect = false;
				gtype d = intrD(s[i][0], s[i][1], s[j][0], s[j][1]);
				if (!equ(d, 0, epsLen)) {
					point r = intrN(s[i][0], s[i][1], s[j][0], s[j][1]) / d;
					intersect = onSeg(s[i][0], s[i][1], r) && onSeg(s[j][0], s[j][1], r);
				}
				if (intersect)
					nSegAfter += 2;
			}
		}
		// output
		if (caseI)
			cout << endl;
		cout << nSegAfter << endl;
	}
	return 0;
}

11343 – Isolated Segments


A simple geometry segment-segment intersection problem.
Given proper functions the problem is straight forward. C++ implementation:

#include <algorithm>
#include <iostream>
#include <complex>
#include <cstdlib>
#include <limits>
using namespace std;

typedef long double gtype;
#define equ(a, b, e) ( abs((a) - (b)) <= (e) )
// const
const gtype len_before_point_digits = 1E6;
const gtype epsLen = numeric_limits<gtype>::epsilon() * len_before_point_digits;
// point
typedef complex<gtype> point;
#define x real()
#define y imag()
// vector
#define dot(a, b) ( (conj(a) * (b)).x )
#define crs(a, b) ( (conj(a) * (b)).y )
// line
#define intrN(a, b, p, q) ( crs((p)-(a),(b)-(a)) * (q) - crs((q)-(a),(b)-(a)) * (p) )
#define intrD(a, b, p, q) ( crs((p)-(a),(b)-(a)) - crs((q)-(a),(b)-(a)) )
#define onLin(a, b, p) ( abs(crs((b) - (a), (p) - (a))) < epsLen )
#define onRay(a, b, p) ( onLin(a, b, p) && dot((b) - (a), (p) - (a)) > -epsLen )
#define onSeg(a, b, p) ( onRay(b, a, p) && dot((b) - (a), (p) - (a)) > -epsLen )
#define pointDist(a, b, p) ( crs((b)-(a), (p)-(a)) / abs((b)-(a)) )

int nCase, nSeg, nIso;
point s[128][2];

int main() {
	cin >> nCase;
	for (int i = 0; i < nCase; ++i) {
		cin >> nSeg;
		// input
		for (int i = 0; i < nSeg; ++i)
			cin >> s[i][0].x >> s[i][0].y >> s[i][1].x >> s[i][1].y;
		// solve
		nIso = 0;
		for (int i = 0; i < nSeg; ++i) {
			bool intersect = false;
			for (int j = 0; j < nSeg && !intersect; ++j)
				if (i != j) {
					gtype d = intrD(s[i][0], s[i][1], s[j][0], s[j][1]);
					if (!equ(d, 0, epsLen)) {
						point r = intrN(s[i][0], s[i][1], s[j][0], s[j][1]) / d;
						intersect = onSeg(s[i][0], s[i][1], r) && onSeg(s[j][0], s[j][1], r);
					} else if (equ(pointDist(s[i][0], s[i][1], s[j][0]), 0, epsLen)) {
						intersect = intersect || onSeg(s[i][0], s[i][1], s[j][0]);
						intersect = intersect || onSeg(s[i][0], s[i][1], s[j][1]);
						intersect = intersect || onSeg(s[j][0], s[j][1], s[i][0]);
						intersect = intersect || onSeg(s[j][0], s[j][1], s[i][1]);
					}
				}
			if (!intersect)
				++nIso;
		}
		// output
		cout << nIso << endl;
	}
	return 0;
}

191 – Intersection


A line-rectangle intersection problem. However, you should read the statement carefully.

The line is said to intersect the rectangle if the line and the rectangle have at least one point in common. The rectangle consists of four straight lines and the area in between.

The rest is straight-froward computations. C++ implementation:

#include <algorithm>
#include <iostream>
#include <complex>
#include <cstdlib>
#include <limits>
using namespace std;

#define equ(a, b, e) ( abs((a) - (b)) <= (e) )
#define inRange(a, b, p, e) ( ((p) >= min(a, b) - (e)) && ((p) <= max(a, b) + (e)) )
typedef double gtype;
// const
const gtype inf = numeric_limits<gtype>::infinity();
const gtype len_before_point_digits = 1E6;
const gtype epsLen = numeric_limits<gtype>::epsilon() * len_before_point_digits;
// point
typedef complex<gtype> point;
#define x real()
#define y imag()
// vector
#define arg(a) ( atan2(a.y, a.x) )
#define dot(a, b) ( (conj(a) * (b)).x )
#define crs(a, b) ( (conj(a) * (b)).y )
// line
#define intersect(a, b, p, q) ( (crs((p)-(a),(b)-(a)) * (q) - crs((q)-(a),(b)-(a)) * (p)) / (crs((p)-(a),(b)-(a)) - crs((q)-(a),(b)-(a))) )
#define onLine(a, b, p) ( abs(crs((b) - (a), (p) - (a))) < epsLen )
#define onRay(a, b, p) ( onLine(a, b, p) && dot((b) - (a), (p) - (a)) > -epsLen )
#define onSeg(a, b, p) ( onRay(b, a, p) && dot((b) - (a), (p) - (a)) > -epsLen )
#define inRect(a, b, p, e) ( inRange((a).x, (b).x, (p).x, e) && inRange((a).y, (b).y, (p).y, e) )

int main() {
	char ans[2][2] = { "F", "T" };
	int nCase, pY, pX;
	point l[2], r[4], p[4];
	cin >> nCase;
	while (nCase--) {
		// input
		for (int i = 0; i < 2; l[i] = point(pX, pY), ++i)
			cin >> pX >> pY;
		cin >> pX >> pY;
		r[3].x = pX;
		r[3].y = pY;
		r[0].x = pX;
		r[2].y = pY;
		cin >> pX >> pY;
		r[1].x = pX;
		r[1].y = pY;
		r[2].x = pX;
		r[0].y = pY;
		// inside
		bool inter = false;
		if (inRect(r[3], r[1], l[0], epsLen) || inRect(r[3], r[1], l[1], epsLen))
			inter = true;
		// intersection
		for (int i = 0; i < 4 && !inter; ++i) {
			p[i] = intersect(l[0], l[1], r[i], r[(i + 1) % 4]);
			if (onSeg(r[i], r[(i + 1) % 4], p[i]) && onSeg(l[0], l[1], p[i]))
				inter = true;
		}
		// output
		cout << ans[inter] << endl;
	}
	return 0;
}