Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Eulers_Formula.mdx #4801

Closed
wants to merge 4 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
308 changes: 252 additions & 56 deletions content/6_Advanced/Eulers_Formula.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
id: eulers-formula
title: "Euler's Formula"
author: Benjamin Qi
author: Benjamin Qi, Aarush Penugonda
description: A formula for finding the number of faces in a planar graph.
prerequisites:
- dsu
Expand All @@ -11,19 +11,215 @@ frequency: 1

## Introduction

<IncompleteSection />
A planar graph is a graph that can be drawn on a plane without any edges crossing. In other words, it can be embedded in the plane such that no two edges intersect except at their endpoints. Planar graphs can be represented in a two-dimensional space without overlaps between edges.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

third sentence is redundant


<Resources>
<Resource
source="MIT"
title=" MIT Course Notes"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird formatting

url="https://dspace.mit.edu/bitstream/handle/1721.1/104426/6-042j-spring-2010/contents/readings/MIT6_042JS10_chap12.pdf"

>
6.024J course notes
</Resource>
<Resource
source="Wiki"
title="Planar Graph"
url="https://en.wikipedia.org/wiki/Planar_graph"
>
Wiki Definition
</Resource>
<Resource
source="Rutgers"
title="Planar Graph"
url="https://sites.math.rutgers.edu/~sk1233/courses/graphtheory-F11/planar.pdf"
/>

</Resources>

## Euler's Formula

Euler's Formula states that any correct embedding of a connected planar graph satistfies:-

<center><h1>V − E + F = 2</h1></center>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use double dollar signs for latex


Where V is the no of vertices, E is the number of edges and F is the number of faces.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use latex for these variables


## Example 1

<Problems problems="e1" />
## Explanation

### Intuition

In this problem, we're asked to count the number of contiguous areas of cells on
several flat rectangles. Such areas are separated by river segments and
rectangle boundaries.

Where else do we count the number of areas on a flat surface?
Comment on lines +59 to +63
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In this problem, we're asked to count the number of contiguous areas of cells on
several flat rectangles. Such areas are separated by river segments and
rectangle boundaries.
Where else do we count the number of areas on a flat surface?
In this problem, we're given a 2D grid with a river segment (path) running through it. Our task is to count the number of contiguous areas of non-river cells on $Q$ subrectangles in this grid.
That's the definition of a planar graph! How can we calculate the number of segments on this plane?

wording is confusing on this part. it might be better to not restate the problem at all


That's right - we use Euler's formula to count the number of faces of a
[planar graph](http://discrete.openmathbooks.org/more/mdm/sec_planar.html). This
suggests that we should turn our rectangles into planar graphs.

### Making the Planar Graph

We can turn a rectangle into a planar graph as so:

- Put temporary river segments outside the border of the rectangle.
- For each river segment, we insert its 4 corners into a set of nodes and its 4
sides into a set of edges.

Notice how the resulting graph is planar, so we can apply Euler's formula.

### Applying Euler's Formula

For a planar graph, Euler's formula is given as $F = E - V + 1 + C$, where $F$
is the number of faces (including the background face), $E$ is the number of
edges, $V$ is the number of vertices, and $C$ is the number of connected
components.

Notice how $F$ in our planar graph is equal to $1 + R + A$, where $R$ is the
number of river segments and $A$ is the answer to the query. This means we must
subtract $R + 1$ from $F$ to get $A$.

Since the whole river is a big connected component, we can just check whether
the river touches the bounding rectangle to determine $C$.

Finding $E$, $V$, and $R$ is a lot more complicated though.

### Finding $E$, $V$, and $R$

To find $E$, $V$, and $R$, we can use a data structure that can handle 2D range
queries efficiently.

However, the coordinates of the grid can get very large, so a simple 2D BIT or
segment tree won't work here.

To work around this, we can either use a 2D BIT with coordinate compression or a
persistent segment tree. See the sections on
[offline 2D sum queries](/plat/2DRQ#2d-offline-sum-queries) or
[persistent segment trees](/adv/persistent) for more details.
Comment on lines +93 to +106
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be useful to tell the user why we need 2d range queries.

also you don't need to split this into another section.


## Implementation

With a persistent segment tree.

**Time Complexity:** $\mathcal{O}(N \log N)$

**Memory Complexity:** $\mathcal{O}(N \log N)$
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Memory Complexity:** $\mathcal{O}(N \log N)$


```cpp
#include "rainbow.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rainbow.h?

#include <bits/stdc++.h>
#define FOR(i, x, y) for (int i = x; i < y; i++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no macros

using namespace std;

const int MAXN = 2e5, MAXSEG = (6e5 + 9) * 19 + 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maxseg?


int cnt = 1, segtree[MAXSEG], left_c[MAXSEG], right_c[MAXSEG];

struct Segtree {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use the one from the module & codesnip it?

set<int> data[MAXN + 1];
int roots[MAXN + 2];

<IncompleteSection />
void add(int x, int y) { data[x].insert(y); }

void build() {
FOR(i, 1, MAXN + 1) {
roots[i + 1] = roots[i];
for (int j : data[i]) update(j, roots[i + 1]);
}
}

void update(int pos, int &node, int l = 1, int r = MAXN) {
segtree[cnt] = segtree[node] + 1;
left_c[cnt] = left_c[node];
right_c[cnt] = right_c[node];
node = cnt++;

if (l == r) return;
int mid = (l + r) / 2;
if (pos > mid) update(pos, right_c[node], mid + 1, r);
else update(pos, left_c[node], l, mid);
}

int query(int l1, int r1, int l2, int r2) {
if (l2 > r2) return 0;
return query(l2, r2, roots[r1 + 1], 1, MAXN) -
query(l2, r2, roots[l1], 1, MAXN);
}
int query(int a, int b, int node, int l, int r) {
if (a > r || b < l) return 0;
if (a <= l && b >= r) return segtree[node];
int mid = (l + r) / 2;
return query(a, b, left_c[node], l, mid) +
query(a, b, right_c[node], mid + 1, r);
}
} vertices, edges_horiz, edges_vert, rivers;

int mx_r, mn_r, mx_c, mn_c;

void add_river(int x, int y) {
vertices.add(x, y);
vertices.add(x + 1, y);
vertices.add(x, y + 1);
vertices.add(x + 1, y + 1);
edges_horiz.add(x, y);
edges_horiz.add(x + 1, y);
edges_vert.add(x, y);
edges_vert.add(x, y + 1);
rivers.add(x, y);
}

void init(int R, int C, int sr, int sc, int M, char *S) {
add_river(sr, sc);
mx_r = mn_r = sr;
mx_c = mn_c = sc;
FOR(i, 0, M) {
if (S[i] == 'N') sr--;
if (S[i] == 'E') sc++;
if (S[i] == 'S') sr++;
if (S[i] == 'W') sc--;
add_river(sr, sc);
mx_r = max(mx_r, sr);
mn_r = min(mn_r, sr);
mx_c = max(mx_c, sc);
mn_c = min(mn_c, sc);
}
vertices.build();
edges_horiz.build();
edges_vert.build();
rivers.build();
}

int colour(int ar, int ac, int br, int bc) {
int E =
edges_horiz.query(ar + 1, br, ac, bc) + edges_vert.query(ar, br, ac + 1, bc);
int V = vertices.query(ar + 1, br, ac + 1, bc);
int R = rivers.query(ar, br, ac, bc);
int C = (ar >= mn_r || br <= mx_r || ac >= mn_c || bc <= mx_c ? 1 : 2);
return E - V + C - R;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use lowercase variable names

}


```

## Example 2

<Problems problems="e2" />

<IncompleteSection />
# Explanation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this h1


This problem involves a 2D grid. The code tracks connected region of points using heights. We will use Disjoint Set Union (DSU). As we process points by increasing height, we merge them into regions and update the answer based on their size. The logic here mirrors an application of Euler's formula for planar graphs, where we maintain
boundaries (faces) as we mege points (vertices) and check their connectivity (edges).
## Implementation

With Euler's Formula

**Time Complexity:** $\mathcal{O}(N^2 \log N)$

**Memory Complexity:** $\mathcal{O}(N^2)$

```cpp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok this entire code block needs to be rewritten -- we want the code to be as readable /easy-to-understand as possible. so this means removing templates/macros

int N, h[750][750];
Expand All @@ -35,67 +231,67 @@ int hsh(int a, int b) { return N * a + b; }
const int xd[4] = {1, 0, -1, 0}, yd[4] = {0, 1, 0, -1};

template <int SZ> struct DSU {
int par[SZ], sz[SZ], measure[SZ];
vi comp[SZ];
DSU() { F0R(i, SZ) par[i] = i, sz[i] = 1, measure[i] = 1; }
bool valid(int b, int c) { return b >= 0 && b < N && c >= 0 && c < N; }
bool ok(int a, int b, int c) {
if (!valid(b, c)) return 0;
return par[hsh(b, c)] == a;
}
void addPoint(int x, pi t) {
par[hsh(t.f, t.s)] = x;
measure[x]++;
F0R(i, 4) {
if (ok(x, t.f + xd[i], t.s + yd[i])) {
measure[x]--;
int j = (i + 1) % 4;
if (ok(x, t.f + xd[j], t.s + yd[j]) &&
ok(x, t.f + xd[j] + xd[i], t.s + yd[j] + yd[i]))
measure[x]++;
}
}
comp[x].pb(hsh(t.f, t.s));
}
void unite(pi x, pi y) { // union-by-rank
int X = hsh(x.f, x.s), Y = hsh(y.f, y.s);
if (par[X] == par[Y]) return;
X = par[X], Y = par[Y];
if (sz(comp[X]) < sz(comp[Y])) swap(X, Y);
trav(t, comp[Y]) addPoint(X, {t / N, t % N});
comp[Y].clear();
}
int par[SZ], sz[SZ], measure[SZ];
vi comp[SZ];
DSU() { F0R(i, SZ) par[i] = i, sz[i] = 1, measure[i] = 1; }
bool valid(int b, int c) { return b >= 0 && b < N && c >= 0 && c < N; }
SansPapyrus683 marked this conversation as resolved.
Show resolved Hide resolved
bool ok(int a, int b, int c) {
if (!valid(b, c)) return 0;
return par[hsh(b, c)] == a;
}
void addPoint(int x, pi t) {
par[hsh(t.f, t.s)] = x;
measure[x]++;
F0R(i, 4) {
if (ok(x, t.f + xd[i], t.s + yd[i])) {
measure[x]--;
int j = (i + 1) % 4;
if (ok(x, t.f + xd[j], t.s + yd[j]) &&
ok(x, t.f + xd[j] + xd[i], t.s + yd[j] + yd[i]))
measure[x]++;
}
}
comp[x].pb(hsh(t.f, t.s));
}
void unite(pi x, pi y) { // union-by-rank
int X = hsh(x.f, x.s), Y = hsh(y.f, y.s);
if (par[X] == par[Y]) return;
X = par[X], Y = par[Y];
if (sz(comp[X]) < sz(comp[Y])) swap(X, Y);
trav(t, comp[Y]) addPoint(X, {t / N, t % N});
comp[Y].clear();
}
};

DSU<750 * 750> D;
bool ok[750][750];

void solve(int x, int y) {
ok[x][y] = 1;
F0R(i, 4) {
int X = x + xd[i], Y = y + yd[i];
if (X < 0 || X >= N || Y < 0 || Y >= N) continue;
if (!ok[X][Y]) continue;
D.unite({x, y}, {X, Y});
}
ok[x][y] = 1;
F0R(i, 4) {
int X = x + xd[i], Y = y + yd[i];
if (X < 0 || X >= N || Y < 0 || Y >= N) continue;
if (!ok[X][Y]) continue;
D.unite({x, y}, {X, Y});
}
}

int main() {
setIO("valleys");
re(N);
F0R(i, N) F0R(j, N) {
re(h[i][j]);
v.pb({h[i][j], {i, j}});
D.comp[hsh(i, j)].pb(hsh(i, j));
}
sort(all(v));
F0R(i, sz(v)) {
solve(v[i].s.f, v[i].s.s);
pi p = v[i].s;
int q = D.par[hsh(p.f, p.s)];
if (D.measure[q] == 1) ans += sz(D.comp[q]);
}
cout << ans;
setIO("valleys");
re(N);
F0R(i, N) F0R(j, N) {
re(h[i][j]);
v.pb({h[i][j], {i, j}});
D.comp[hsh(i, j)].pb(hsh(i, j));
}
sort(all(v));
F0R(i, sz(v)) {
solve(v[i].s.f, v[i].s.s);
pi p = v[i].s;
int q = D.par[hsh(p.f, p.s)];
if (D.measure[q] == 1) ans += sz(D.comp[q]);
}
cout << ans;
}
```

Expand Down