본문으로 건너뛰기
김신건의 로그

함수 그래프 (Functional Graph)

· 수정 · 📖 약 2분 · 690자/단어 #algorithm #graph #functional-graph #cycle-detection
functional graph, 함수 그래프, successor graph

정의

함수 그래프 (Functional Graph) 는 정점 집합 V 와 함수 f: V → V 로 정의되는 방향 그래프. 각 정점 v 에서 정확히 하나의 간선 v → f(v) 가 나간다. 형태는 여러 사이클 (cycle) + 각 사이클로 들어가는 트리 (tree) 구조.

문제 상황과 동기

PS 에서 “다음 칸으로 이동”, “연결된 정점으로 한 단계” 문제는 out-degree 가 정확히 1 인 그래프. BFS/DFS 로 naive 하게 x 번 진행하면 O(x · N), 구간이 10^9 이상이면 터진다. 핵심 통찰: 경로는 반드시 “tree part → cycle entry → cycle 회전”, 세 구간으로 분해. Floyd’s cycle finding (토끼와 거북) 으로 사이클 길이 + 시작점 O(N) 에 찾고, 나머지는 prefix jump 또는 modular 계산.

자주 등장: BOJ 상태 전이 시뮬레이션, “N 번 이동 후 위치”, “언제 사이클 만나는가”, 해시 충돌 분석, 수열 반복 규칙.

시각화

핵심 아이디어

invariant

정점 v 에서 출발하면 경로는 tree tail → cycle 두 단계.

v → f(v) → f²(v) → ... → entry → [cycle 시작] → entry (다시)

사이클 진입까지 거리 λ (lambda), 사이클 길이 μ (mu).

f^k(v) = {
    tree 안,           k < λ
    cycle 회전,        k ≥ λ,  (k - λ) mod μ 위치
}

Floyd’s cycle finding

토끼 (2배 속도), 거북 (1배 속도):

  1. phase 1: 토끼가 사이클 한 바퀴 돌 동안 거북을 따라잡으면 사이클 존재 확정.
  2. phase 2: 토끼를 시작점으로, 둘 다 1 step 씩 움직여 만나면 그 점이 entry.
  3. phase 3: entry 에서 한 바퀴 돌면 μ.

시간: O(λ + μ), 공간 O(1). 비순환 그래프면 null 이나 범위 밖 도달.

binary lifting (optional)

k 가 큰 경우 f^k(v) 를 O(log k) 에 구하려면:

dp[v][i] = f^(2^i)(v)

전처리 O(N log N), 쿼리 O(log k).

알고리즘

Floyd_cycle(f, start):
    tortoise = start
    hare = start
    # phase 1: 사이클 감지
    loop
        tortoise = f(tortoise)
        hare = f(f(hare))
        if hare == tortoise: break
        if hare null or f(hare) null: return (λ=∞, μ=0)  # 비순환
    # phase 2: entry 찾기
    tortoise = start
    while tortoise ≠ hare:
        tortoise = f(tortoise)
        hare = f(hare)
    entry = tortoise
    # phase 3: μ 측정
    μ = 1
    hare = f(entry)
    while hare ≠ entry:
        hare = f(hare)
        μ++
    # λ 측정
    λ = 0
    p = start
    while p ≠ entry:
        p = f(p)
        λ++
    return (λ, μ, entry)

구현

// Functional graph: Floyd's cycle detection
#include <bits/stdc++.h>
using namespace std;

struct Cycle {
  int lambda, mu, entry;
};

Cycle floyd(const vector<int>& f, int start) {
  auto step = [&](int x) { return (x >= (int)f.size() || x < 0) ? -1 : f[x]; };
  int t = start, h = start;
  // phase 1
  do {
      t = step(t);
      h = step(step(h));
      if (h < 0 || t < 0) return {-1, 0, -1};
  } while (t != h);
  // phase 2
  int entry = start;
  while (entry != h) { entry = step(entry); h = step(h); }
  // phase 3
  int mu = 1;
  for (int x = step(entry); x != entry; x = step(x)) mu++;
  int lambda = 0;
  for (int x = start; x != entry; x = step(x)) lambda++;
  return {lambda, mu, entry};
}

int main() {
  int n, s; cin >> n >> s;
  vector<int> f(n);
  for (auto& v : f) cin >> v;
  auto [lam, mu, ent] = floyd(f, s);
  cout << "lambda=" << lam << " mu=" << mu << " entry=" << ent << "\n";
}
stdin
6 0
1 2 3 4 2 5
결과
lambda=2 mu=3 entry=2

복잡도

항목
사이클 탐지O(λ + μ) 시간, O(1) 공간
f^k(v) 단일O(k) naive, O(log k) binary lifting
전처리 (lifting)O(N log N) 시간, O(N log N) 공간
쿼리 (lifting)O(log k)

변형 / 활용

1. 상태 전이 시뮬레이션

“N 번 이동 후 위치” 문제. k < λ 면 순회, k ≥ λ 면 (k - λ) mod μ.

2. 연결 요소 분석

각 정점에서 Floyd 돌려 entry 찾으면, 같은 entry 를 공유하는 정점끼리 한 “weakly connected component”.

3. 사이클 없는 경우

hare 가 null 이면 무한히 뻗어나가거나 단절. 예: 해시 체인.

4. 다중 출발

여러 시작점이 같은 사이클로 모이는가? entry 기준 그룹.

함정

1. out-degree ≠ 1

일반 그래프라면 functional graph 가 아님. 이 기법 안 먹힘.

2. 범위 체크

f[v] 가 배열 밖이면 비순환. step 함수 null 처리.

3. λ 계산 빠뜨림

phase 1~3 은 μ, entry 만 줌. λ 는 start → entry 거리 따로.

4. k 매우 큰 경우

k > 10^9 면 naive loop 불가. binary lifting 또는 mod μ 쓰기.

BOJ 연습 문제

번호제목정답률링크
BOJ 2331반복수열-kokoa-lab
BOJ 9466텀 프로젝트-kokoa-lab
BOJ 15591MooTube (Silver)-kokoa-lab
BOJ 16958텔레포트-kokoa-lab

참고

이 글의 용어 (3개)
깊이 우선 탐색 (DFS)algorithm
정의 깊이 우선 탐색 (Depth-First Search, DFS) 는 그래프 G=(V, E) 에서 갈 수 있는 만큼 깊이 들어가다가 막히면 백트래킹하는 알고리즘. 스택 (LIF…
Binary Lifting: 2의 지수 doublingalgorithm
정의 트리에서 정점의 2^k 조상 을 O(N log N) 전처리 후 O(log N) 쿼리로 답하는 기법. LCA, k-th ancestor 등에 사용. 전처리 LCA 쿼리 O(l…
Cycle Detection: 그래프 사이클 탐지algorithm
정의 그래프에 사이클이 존재하는가, 또는 어느 사이클인가 를 찾는 문제. 무향 그래프 Union-Find 접근 간선 (u, v) 삽입 시 이면 사이클. DFS 접근 부모가 아닌 …

이 개념을 다룬 위키 페이지 (1)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기