오일러 투어 테크닉

2025. 2. 8. 23:14C++ study

아이디어!

  • 트리에서 어떤 정점이 존재할 때 이 정점을 구간에 대해서 표현하고 싶을 때 오일러 투어 테크닉을 사용한다.

예를 들어 위와 같은 그림이 있다고 하자!

여기에서 2번 정점을 살펴보면 2번은 3,4,5의 정점을 포함하고 있음을 확인할 수 있다.

즉 우리는 오일러 투어 테크닉을 활용한다면 시작점은 2로 표시해주고 끝점을 5로 지정하여서 정점을 구간에 대해서 표현할 수 있다!

1번 정점과 같은 경우에도 시작점은 1이고 끝점을 5로 하여서 구간에 대해서 표현할 수 있다.

적용!

  • dfs를 활용하면 깊이를 우선으로 탐색하기 때문에 각 정점의 범위를 기록하기 유용하다!
  • 트리의 루트에서 dfs를 시작하여서 각 노드를 처음 방문할 때, 그리고 다시 돌아올 때를 기록해 준다.
  • 오일러 투어 테크닉을 구현할 때는 출발과 끝을 기록해 줄 배열 두 개가 필요하다.
  • 알고리즘을 코드에 적용해 본다면 다음과 같다.
//BOJ 2820 자동차 공장

#include<bits/stdc++.h>
#define INF 500004
using namespace std;
int n, m, cnt;

vector<int> v[INF];
long long cost[INF], t[INF * 4], lazy[INF * 4];
int s[INF], e[INF];

void propagation(int node, int l, int r) {
    if (lazy[node]) {
        t[node] += lazy[node] * (r - l + 1);
        if (l != r) {
            lazy[node * 2] += lazy[node];
            lazy[node * 2 + 1] += lazy[node];
        }
        lazy[node] = 0;
    }
}

void update(int node, int l, int r, int x, int y, int val) {
    propagation(node, l, r);
    if (y < l || r < x) return;
    if (x <= l && r <= y) {
        lazy[node] += val;
        propagation(node, l, r);
        return;
    }
    int middle = (l + r) / 2;
    update(node * 2, l, middle, x, y, val);
    update(node * 2 + 1, middle + 1, r, x, y, val);
    t[node] = t[node * 2] + t[node * 2 + 1];
}

long long query(int node, int l, int r, int idx) {
    propagation(node, l, r);
    if (l == r) return t[node];
    int middle = (l + r) / 2;
    if (idx <= middle) return query(node * 2, l, middle, idx);
    else return query(node * 2 + 1, middle + 1, r, idx);
}

void find_boss(int idx) { //오일러 경로 테크닉
    s[idx] = ++cnt;
    for (int i : v[idx]) find_boss(i);
    e[idx] = cnt;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int boss;
        if (i == 1) cin >> cost[i];
        else {
            cin >> cost[i] >> boss;
            v[boss].push_back(i);
        }
    }

    find_boss(1);

    for (int i = 1; i <= n; i++) update(1, 1, n, s[i], s[i], cost[i]);

    for (int i = 1; i <= m; i++) {
        char c;
        cin >> c;

        if (c == 'p') {
            int x, y;
            cin >> x >> y;
            update(1, 1, n, s[x] + 1, e[x], y);
        } else {
            int x;
            cin >> x;
            cout << query(1, 1, n, s[x]) << "\n";
        }
    }
}
  • 이 코드는 오일러 테크닉 투어를 통해서 구간을 구한 다음에 각 구간에 대해서 업데이트를 하거나 쿼리를 구하는 방식으로 작동하는 코드이다. 
  • 여기에서 오일러 투어 테크닉 부분을 확인해 본다면 다음과 같다.
void find_boss(int idx) { //오일러 경로 테크닉
    s[idx] = ++cnt;
    for (int i : v[idx]) find_boss(i);
    e[idx] = cnt;
}
  • 특정한 정점에서 자신을 포함한 하위 정점을 구하는 데 활용할 수 있다.
  • dfs를 활용한다면 자신의 부하 정점을 모두 방문한 다음에 현제의 정점으로 돌아오게 되면서 범위를 확인할 수 있게 된다.
cf)
하지만 양방향 간선으로 주어지게 된다면 위의 코드에 중복 반복을 방지하기 위해서 parent변수를 추가해 준다!
void find_boss(int idx, int parent) { //오일러 경로 테크닉
    s[idx] = ++cnt;
    for (int i : v[idx]) {
          if(i!=parent) find_boss(i, x);
    }
    e[idx] = cnt;
}​

 

'C++ study' 카테고리의 다른 글

Meet In The Middle  (0) 2025.02.12
피보나치 수(Algorithm)  (0) 2025.02.10
오일러 함수  (2) 2025.02.08
페르마의 소정리  (0) 2025.02.07
분할 정복을 이용한 거듭제곱  (6) 2025.02.07