lorenzotinfena's blog

By lorenzotinfena, history, 7 months ago, In English

Would be better in regular rounds to give penalty only when that problem is not accepted at least one time?

Full text and comments »

  • Vote: I like it
  • -1
  • Vote: I do not like it

By lorenzotinfena, 7 months ago, In English

Given $$$n$$$ $$$(2\le n \le 2\cdot 10^5)$$$ distinct points ($$$-10^9\le p_x, p_y\le 10^9$$$) of a two-dimensional plane, find the maximum area of any possible rectangle identified by pairs of points as opposite angles.

This problem is almost identical to this problem so the following tutorial is also valid for that problem with slight modifications.

Editorial:

First of all I simplify the problem by searching for the maximum area formed only by vertices in upper-left and bottom-right corner, and then, invert all $$$y$$$ coordinate of all points, and repeat the search to consider also the rectangles formed by upper-right and bottom-left points.

Notation:

  • Let's call the plane $$$P\subseteq R^2$$$.
  • Given $$$p, q \in P$$$, $$$p\le q \iff p_x\le q_x\land p_y\le q_y$$$

Definitions:

  • upper-left border= $$${p\in P | \nexists q \text{ s.t. } q_x\le p_x\land q_y\ge p_y}$$$
  • bottom-right border is defined similarly
  • Let's define $$$f$$$ to be the area of the rectangle identified by $$$p$$$ as upper-left corner and $$$q$$$ as bottom-right corner, so $$$f(p, q \in P)=(p_x-q_x)(p_y-q_y)$$$ (Note that it can be negative)

It can be proved that an optimal (with maximum area) rectangle can be formed with a pair of points $$$(p\in $$$ upper-left border $$$, q \in $$$ bottom-right border $$$)$$$

Theorem:

Given 4 points $$$a,b,c,d\in P$$$ such that $$$a\le b\land c \le d$$$

$$$f(b,c)\ge f(b,d)\implies f(a,c)\ge f(a,d)$$$

Proof:

$$$f(b,c)\ge f(b,d)\implies$$$
$$$(c_x-b_x)(b_y-c_y)\ge (d_x-b_x)(b_y-d_y)\implies$$$
$$$c_xb_y+b_xc_y-c_xc_y\ge d_xb_y+b_xd_y-d_xd_y\implies$$$
$$$c_xa_y+b_xc_y-c_xc_y\ge d_xa_y+b_xd_y-d_xd_y\implies$$$
$$$c_xa_y+a_xc_y-c_xc_y\ge d_xa_y+a_xd_y-d_xd_y\implies$$$
$$$f(a,c)\ge f(a,d)$$$

We can use this theorem to optimize our search. We search recursively on ranges of type (upper-left border L, upper-left border R, bottom-right border L, bottom-right border R).

We can maintain a stack/queue of ranges/quadruples to consider, the first range/quadruple is (0, length(upper-left border)-1, 0, length(bottom-right border)-1)

We can use this theorem to split a upper-left border range in a quadruple in half at worst case.

We can do that by taking the middle element in the upper-left border, iterate through the bottom-right border and search for the maximum $$$f$$$, let its index be $$$i$$$.

Then we can split our search considering recursively the ranges (0, m-1, 0, i) and (m+1, length(upper-left border)-1, i, length(bottom-right border)-1).

Now we repeat the process in a similar way until the stack/queue is empty.

The worst case the time complexity is $$$O(nlogn)$$$.

Edge case: when the maximum area given in the first search of an iteration is negative, it can be seen that we can simply split between the last element with the x-coordinate less than the x-coordinate of the middle element and the next element.

Implementation:

func computeLargestRectanglenlogn(points [][2]int) int {
	dist := func(p1, p2 [2]int) int {
		return math.Abs((p1[0] - p2[0]) * (p1[1] - p2[1]))
	}
	helper := func(points [][2]int) int {
		upperLeft := [][2]int{}
		maxYSoFar := int(-1e9 - 1)
		for i := 0; i < len(points); i++ {
			if points[i][1] > maxYSoFar {
				upperLeft = append(upperLeft, points[i])
				maxYSoFar = points[i][1]
			}
		}

		bottomRight := [][2]int{}
		minYSoFar := int(1e9 + 1)
		for i := len(points) - 1; i >= 0; i-- {
			if points[i][1] < minYSoFar {
				bottomRight = append(bottomRight, points[i])
				minYSoFar = points[i][1]
			}
		}
		for i, j := 0, len(bottomRight)-1; i < j; i, j = i+1, j-1 {
			bottomRight[i], bottomRight[j] = bottomRight[j], bottomRight[i]
		}

		type Range struct {
			upperLeftL   int
			upperLeftR   int
			bottomRightL int
			bottomRightR int
		}
		ranges := []Range{{0, len(upperLeft) - 1, 0, len(bottomRight) - 1}}
		maxSoFar := 0
		for len(ranges) != 0 {
			ran := ranges[len(ranges)-1]
			ranges = ranges[:len(ranges)-1]

			m := (ran.upperLeftL + ran.upperLeftR) / 2

			bestIbottomRight := -1
			maxSize := -1
			lastIndexOnTheLeftOfBestBottomRight := ran.bottomRightL - 1
			for i := ran.bottomRightL; i <= ran.bottomRightR; i++ {
				if upperLeft[m][0] > bottomRight[i][0] {
					lastIndexOnTheLeftOfBestBottomRight = i
				} else {
					size := dist(upperLeft[m], bottomRight[i])
					if size > maxSize && upperLeft[m][1] > bottomRight[i][1] {
						maxSize = size
						bestIbottomRight = i
					}
				}
			}

			isValid := func(ran Range) bool {
				return ran.upperLeftL <= ran.upperLeftR && ran.bottomRightL <= ran.bottomRightR
			}

			if bestIbottomRight != -1 {

				maxSoFar = math.Max(maxSoFar, maxSize)

				tmp := Range{ran.upperLeftL, m - 1, ran.bottomRightL, bestIbottomRight}
				if isValid(tmp) {
					ranges = append(ranges, tmp)
				}
				tmp = Range{m + 1, ran.upperLeftR, bestIbottomRight, ran.bottomRightR}
				if isValid(tmp) {
					ranges = append(ranges, tmp)
				}
			} else {
				tmp := Range{ran.upperLeftL, m - 1, ran.bottomRightL, lastIndexOnTheLeftOfBestBottomRight}
				if isValid(tmp) {
					ranges = append(ranges, tmp)
				}
				tmp = Range{m + 1, ran.upperLeftR, lastIndexOnTheLeftOfBestBottomRight + 1, ran.bottomRightR}
				if isValid(tmp) {
					ranges = append(ranges, tmp)
				}
			}
		}
		return maxSoFar
	}

	sort.Slice(points, func(i, j int) bool {
		return points[i][0] < points[j][0]
	})
	tmp := helper(points)
	for i := 0; i < len(points); i++ {
		points[i] = [2]int{points[i][0], -points[i][1]}
	}
	return math.Max(tmp, helper(points))
}

Alternative solution: https://stackoverflow.com/a/69276749

Full text and comments »

  • Vote: I like it
  • +23
  • Vote: I do not like it

By lorenzotinfena, history, 8 months ago, In English

In my previous 2 blogs (blog1 blog2) I presented this template for golang. Read the previous 2 blogs if you don't know it.

The problem with this template was that at some point the file size gets too large in some problems and codeforces didn't accept it anymore, so now I added this feature that automatically detect all the dead code and removes it from the code.

Full text and comments »

  • Vote: I like it
  • 0
  • Vote: I do not like it

By lorenzotinfena, history, 8 months ago, In English

Hi! I created this template: https://github.com/lorenzotinfena/competitive-go . For input it uses directly syscall.Read (I'm planning to do the same also for the output), and then I added a little setting (very little detail) that can lower the time, but increase the memory, or the contrary or keeping balanced, it uses debug.SetGCPercent .

Full text and comments »

  • Vote: I like it
  • +25
  • Vote: I do not like it

By lorenzotinfena, 15 months ago, In English

With a non standard library!

It basically recursively copy paste in one file all non standard packages imported.

https://github.com/lorenzotinfena/competitive-go

Example here: https://codeforces.net/contest/1857/submission/217917118

Full text and comments »

  • Vote: I like it
  • 0
  • Vote: I do not like it