Islands
Single Water Level: Flood Fill Mark each cell as land if elevation[r][c] > L. Count connected components of land cells using BFS. Multiple Water Levels: Offline DSU To answer Q queries efficiently: Sort all cells by e...
Problem Statement
Rendered from the "Problem Statement" section in the LaTeX write-up.
Given an $R \times C$ elevation grid and a water level $L$, a cell is land if its elevation is strictly greater than $L$, and water otherwise. Count the number of islands (maximal connected components of land cells under 4-adjacency).
An extended version asks for island counts at multiple water levels.
Editorial
The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.
Solution
Single Water Level: Flood Fill
Mark each cell as land if $\text{elevation}[r][c] > L$.
Count connected components of land cells using BFS.
Multiple Water Levels: Offline DSU
To answer $Q$ queries efficiently:
Sort all cells by elevation in decreasing order.
Process cells one by one. When a cell ``emerges'' above the current water level, activate it in a Disjoint Set Union (DSU) structure and merge it with any already-active 4-neighbors.
Record the component count at each distinct elevation threshold.
Answer each query by looking up the component count at the appropriate threshold.
Complexity Analysis
Single water level: $O(RC)$ time and space.
Multiple water levels: $O(RC\,\alpha(RC) + Q\log(RC))$ total, where $\alpha$ is the inverse Ackermann function.
Implementation: Single Water Level
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main() {
int R, C, L;
cin >> R >> C >> L;
vector<vector<int>> elev(R, vector<int>(C));
for (int i = 0; i < R; i++)
for (int j = 0; j < C; j++)
cin >> elev[i][j];
vector<vector<bool>> visited(R, vector<bool>(C, false));
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
int islands = 0;
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (elev[i][j] > L && !visited[i][j]) {
islands++;
queue<pair<int,int>> q;
q.push({i, j});
visited[i][j] = true;
while (!q.empty()) {
auto [r, c] = q.front(); q.pop();
for (int d = 0; d < 4; d++) {
int nr = r + dx[d], nc = c + dy[d];
if (nr >= 0 && nr < R && nc >= 0 && nc < C
&& !visited[nr][nc]
&& elev[nr][nc] > L) {
visited[nr][nc] = true;
q.push({nr, nc});
}
}
}
}
}
}
cout << islands << endl;
return 0;
}
Implementation: Multiple Water Levels (DSU)
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
struct DSU {
vector<int> parent, rank_;
int components;
DSU(int n) : parent(n, -1), rank_(n, 0), components(0) {}
void activate(int x) {
parent[x] = x;
components++;
}
bool active(int x) const { return parent[x] != -1; }
int find(int x) {
while (parent[x] != x) {
parent[x] = parent[parent[x]]; // path splitting
x = parent[x];
}
return x;
}
void unite(int a, int b) {
a = find(a); b = find(b);
if (a == b) return;
if (rank_[a] < rank_[b]) swap(a, b);
parent[b] = a;
if (rank_[a] == rank_[b]) rank_[a]++;
components--;
}
};
int main() {
int R, C;
cin >> R >> C;
vector<vector<int>> elev(R, vector<int>(C));
vector<pair<int,int>> cells; // (elevation, flat_index)
for (int i = 0; i < R; i++)
for (int j = 0; j < C; j++) {
cin >> elev[i][j];
cells.push_back({elev[i][j], i * C + j});
}
// Sort by decreasing elevation
sort(cells.begin(), cells.end(), greater<>());
DSU dsu(R * C);
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
// Process cells from highest to lowest elevation.
// After processing all cells with elevation > L,
// dsu.components gives the island count at water level L.
map<int, int> levelIslands;
int i = 0;
while (i < (int)cells.size()) {
int e = cells[i].first;
// Process all cells with this elevation
int j = i;
while (j < (int)cells.size() && cells[j].first == e) {
int idx = cells[j].second;
int r = idx / C, c = idx % C;
dsu.activate(idx);
for (int d = 0; d < 4; d++) {
int nr = r + dx[d], nc = c + dy[d];
if (nr >= 0 && nr < R && nc >= 0 && nc < C) {
int nidx = nr * C + nc;
if (dsu.active(nidx))
dsu.unite(idx, nidx);
}
}
j++;
}
// Water level = e - 1 means all cells with elev > e-1
// (i.e., elev >= e) are land.
levelIslands[e - 1] = dsu.components;
i = j;
}
int Q;
cin >> Q;
while (Q--) {
int L;
cin >> L;
auto it = levelIslands.upper_bound(L);
if (it == levelIslands.begin())
cout << 0 << "\n";
else {
--it;
cout << it->second << "\n";
}
}
return 0;
}
Notes
The single-water-level solution is a straightforward BFS flood fill.
The DSU solution processes cells in decreasing elevation order. When a batch of cells at elevation $e$ is activated, the resulting component count equals the number of islands for any water level in $[e-1, e')$ where $e'$ is the next lower elevation present.
Path splitting (iterative path compression) is used instead of recursive path compression to avoid stack overflow on large inputs.
Code
C++ solution used for this page.
// IOI 1992 - Problem 3: Islands
// Given elevation grid and water level queries, count islands (cells > water level).
// DSU approach: process cells in decreasing elevation, answer queries via map.
#include <bits/stdc++.h>
using namespace std;
struct DSU {
vector<int> parent, rnk;
int components;
DSU(int n) : parent(n, -1), rnk(n, 0), components(0) {}
void activate(int x) {
parent[x] = x;
components++;
}
bool active(int x) { return parent[x] != -1; }
int find(int x) {
while (parent[x] != x) x = parent[x] = parent[parent[x]];
return x;
}
void unite(int a, int b) {
a = find(a); b = find(b);
if (a == b) return;
if (rnk[a] < rnk[b]) swap(a, b);
parent[b] = a;
if (rnk[a] == rnk[b]) rnk[a]++;
components--;
}
};
int main() {
int R, C;
scanf("%d%d", &R, &C);
vector<vector<int>> elev(R, vector<int>(C));
vector<pair<int,int>> cells;
for (int i = 0; i < R; i++)
for (int j = 0; j < C; j++) {
scanf("%d", &elev[i][j]);
cells.push_back({elev[i][j], i * C + j});
}
// Sort by decreasing elevation
sort(cells.begin(), cells.end(), greater<pair<int,int>>());
DSU dsu(R * C);
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
// Build map: water_level -> island_count
map<int, int> levelIslands;
int prevElev = cells[0].first + 1;
for (auto& [e, idx] : cells) {
if (e != prevElev) {
levelIslands[prevElev - 1] = dsu.components;
prevElev = e;
}
int r = idx / C, c = idx % C;
dsu.activate(idx);
for (int d = 0; d < 4; d++) {
int nr = r + dx[d], nc = c + dy[d];
if (nr >= 0 && nr < R && nc >= 0 && nc < C) {
int nidx = nr * C + nc;
if (dsu.active(nidx))
dsu.unite(idx, nidx);
}
}
}
levelIslands[prevElev - 1] = dsu.components;
// Answer queries
int Q;
scanf("%d", &Q);
while (Q--) {
int L;
scanf("%d", &L);
auto it = levelIslands.upper_bound(L);
if (it == levelIslands.begin())
printf("0\n");
else {
--it;
printf("%d\n", it->second);
}
}
return 0;
}
Source Files and Assets
Raw files are still available here when you want the original TeX, C++, or statement assets.