IOI 1996
IOI 1996

Network of Schools

Problem Statement A network of n schools is connected by one-way links. Software distributed to a school propagates transitively along outgoing links. Task A: Find the minimum number of schools that must receive the s...

Updated May 21, 2026
Track IOI
Year 1996
Statement Rendered from TeX
TeXC++Rendered statement

Problem Statement

Rendered from the "Problem Statement" section in the LaTeX write-up.

A network of $n$ schools is connected by one-way links. Software distributed to a school propagates transitively along outgoing links.

  • Task A: Find the minimum number of schools that must receive the software initially so that all schools eventually receive it.

  • Task B: Find the minimum number of one-way links to add so that distributing software to any single school causes all schools to receive it (i.e., make the graph strongly connected).

  • Constraints: $2 \le n \le 100$.

Editorial

The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.

Solution Approach

Step 1: Strongly Connected Components

Use Kosaraju's algorithm (or Tarjan's) to compute the SCCs. Condense the graph into a DAG where each node represents an SCC.

Step 2: Analyze the Condensed DAG

Let $S$ denote the number of SCCs.

  • Task A: The answer is the number of source nodes (in-degree 0) in the condensed DAG. These SCCs cannot be reached from any other SCC, so each must receive software directly: \[ \text{Answer A} = |\{v : \deg^-(v) = 0\}|. \]

  • Task B: To make the condensed DAG strongly connected, we must eliminate all sources and sinks. The minimum number of edges required is: \[ \text{Answer B} = \max(\text{sources}, \text{sinks}), \] where sources and sinks refer to nodes with in-degree 0 and out-degree 0 in the condensed DAG, respectively.

Proof (Justification for Task B).

Each added edge can eliminate at most one source and one sink. Therefore $\max(\text{sources}, \text{sinks})$ is a lower bound. A matching construction (pairing sources with sinks and connecting them cyclically) achieves this bound.

Special case: If $S = 1$ (the graph is already strongly connected), then Answer A = 1 and Answer B = 0.

C++ Solution

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

const int MAXN = 105;
int n;
vector<int> adj[MAXN], radj[MAXN];
int comp[MAXN];
bool visited[MAXN];
int order_arr[MAXN], order_cnt;

// Kosaraju's Algorithm -- Pass 1: forward DFS, record finish order
void dfs1(int u) {
    visited[u] = true;
    for (int v : adj[u])
        if (!visited[v]) dfs1(v);
    order_arr[order_cnt++] = u;
}

// Kosaraju's Algorithm -- Pass 2: reverse DFS, assign component IDs
void dfs2(int u, int c) {
    comp[u] = c;
    for (int v : radj[u])
        if (comp[v] == -1) dfs2(v, c);
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        int v;
        while (scanf("%d", &v) == 1 && v != 0) {
            adj[i].push_back(v);
            radj[v].push_back(i);
        }
    }

    // Pass 1
    memset(visited, false, sizeof(visited));
    order_cnt = 0;
    for (int i = 1; i <= n; i++)
        if (!visited[i]) dfs1(i);

    // Pass 2
    memset(comp, -1, sizeof(comp));
    int numSCC = 0;
    for (int i = order_cnt - 1; i >= 0; i--)
        if (comp[order_arr[i]] == -1)
            dfs2(order_arr[i], numSCC++);

    if (numSCC == 1) {
        printf("1\n0\n");
        return 0;
    }

    // Build condensed DAG: determine which SCCs have incoming/outgoing edges
    // We only need to know whether in-degree and out-degree are zero,
    // so duplicate edges between the same pair of SCCs do not affect the result.
    bool hasIn[MAXN] = {}, hasOut[MAXN] = {};
    for (int u = 1; u <= n; u++)
        for (int v : adj[u])
            if (comp[u] != comp[v]) {
                hasIn[comp[v]] = true;
                hasOut[comp[u]] = true;
            }

    int sources = 0, sinks = 0;
    for (int i = 0; i < numSCC; i++) {
        if (!hasIn[i]) sources++;
        if (!hasOut[i]) sinks++;
    }

    printf("%d\n%d\n", sources, max(sources, sinks));
    return 0;
}

Complexity Analysis

  • Time complexity: $O(n + m)$ where $m$ is the number of edges. Kosaraju's algorithm runs two DFS passes, each $O(n + m)$. Computing sources and sinks in the condensed DAG is $O(n + m)$.

  • Space complexity: $O(n + m)$ for the adjacency lists and reverse graph.

Code

C++ solution used for this page.

C++

Clean code view with a raw-file link when you want the original source.

Raw file
// IOI 1996 - Network of Schools
// Kosaraju's SCC + condensed DAG analysis
// Task A: count sources (in-degree 0) in condensed DAG
// Task B: max(sources, sinks) to make graph strongly connected
// Time: O(n + m), Space: O(n + m)
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 105;
int n;
vector<int> adj[MAXN], radj[MAXN];
int comp[MAXN];
bool visited[MAXN];
int order_arr[MAXN], order_cnt;

void dfs1(int u) {
    visited[u] = true;
    for (int v : adj[u])
        if (!visited[v]) dfs1(v);
    order_arr[order_cnt++] = u;
}

void dfs2(int u, int c) {
    comp[u] = c;
    for (int v : radj[u])
        if (comp[v] == -1) dfs2(v, c);
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        int v;
        while (scanf("%d", &v) == 1 && v != 0) {
            adj[i].push_back(v);
            radj[v].push_back(i);
        }
    }

    // Kosaraju's SCC
    memset(visited, false, sizeof(visited));
    order_cnt = 0;
    for (int i = 1; i <= n; i++)
        if (!visited[i]) dfs1(i);

    memset(comp, -1, sizeof(comp));
    int numSCC = 0;
    for (int i = order_cnt - 1; i >= 0; i--)
        if (comp[order_arr[i]] == -1)
            dfs2(order_arr[i], numSCC++);

    if (numSCC == 1) {
        printf("1\n0\n");
        return 0;
    }

    // Build condensed DAG, track which SCC pairs have edges
    set<pair<int,int>> condEdges;
    bool hasIn[MAXN] = {}, hasOut[MAXN] = {};
    for (int u = 1; u <= n; u++)
        for (int v : adj[u])
            if (comp[u] != comp[v]) {
                auto e = make_pair(comp[u], comp[v]);
                if (condEdges.insert(e).second) {
                    hasIn[comp[v]] = true;
                    hasOut[comp[u]] = true;
                }
            }

    int sources = 0, sinks = 0;
    for (int i = 0; i < numSCC; i++) {
        if (!hasIn[i]) sources++;
        if (!hasOut[i]) sinks++;
    }

    printf("%d\n%d\n", sources, max(sources, sinks));
    return 0;
}

Source Files and Assets

Raw files are still available here when you want the original TeX, C++, or statement assets.

Show raw files