적용된 알고리즘과 문제 해결 전략

총점: \(100+100+30+45=275\) 시작 10분 동안 문제 A에 접근했으나, 한 시간 후 포기하고 다른 문제로 이동. 문제 B를 빠르게 해결한 후 C에 도전하였으나 실패. D의 폭력적인 해법을 작성하고 남은 30분 동안 다시 A를 시도.

A: H 군의 블록

상단과 하단의 숫자를 모두 세그먼트 트리에 추가합니다. 만약 숫자가 두 번 이상 등장하면 이를 후보로 설정하며, 각 단계에서 최소 값을 선택하면서 그와 인접한 숫자들을 포함시키는 방식으로 진행합니다.

코드 보기

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct SegmentTree {
    int l, r, cnt;
    pair<int, int> mn;
} tr[800005];

void build(int l, int r, int i) {
    tr[i] = {l, r, 0, {INT_MAX, 0}};
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(l, mid, i << 1);
    build(mid + 1, r, i << 1 | 1);
}

void update(int p, int k, int i) {
    if (tr[i].l == tr[i].r) {
        tr[i].cnt += k;
        if (tr[i].cnt >= 2) tr[i].mn = {p, tr[i].l};
        else tr[i].mn = {INT_MAX, 0};
        return;
    }
    if (p <= tr[i << 1].r) update(p, k, i << 1);
    else update(p, k, i << 1 | 1);
    tr[i].mn = min(tr[i << 1].mn, tr[i << 1 | 1].mn);
}

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> a[m + 1], pos[n + 1];
    for (int i = 1; i <= m; ++i) {
        int k;
        cin >> k;
        while (k--) {
            int x;
            cin >> x;
            a[i].push_back(x);
            pos[x].push_back({i, a[i].size() - 1});
        }
    }
    for (int i = 1; i <= n; ++i) sort(pos[i].begin(), pos[i].end());
    build(1, n, 1);
    for (int i = 1; i <= m; ++i) {
        update(a[i][0], 1, 1);
        if (a[i].size() > 1) update(a[i].back(), 1, 1);
    }
    // 결과 출력 부분 생략
}

B: H 군의 트리

해결 방법은 트리의 간선을 끊거나 사이클을 분리하는 것입니다. 사이클을 체인으로 변환하여 투 포인터 기법을 사용할 수 있습니다.

코드 보기

#include <bits/stdc++.h>
using namespace std;

struct Edge { int to, next; };
Edge e[400005];
int head[200005], idx = 0, size[200005], ans = INT_MAX;

void addEdge(int u, int v) {
    e[++idx] = {v, head[u]};
    head[u] = idx;
}

void dfs(int u, int fa) {
    size[u] = a[u];
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if (v != fa) {
            dfs(v, u);
            size[u] += size[v];
        }
    }
    if (!inCycle[u]) ans = min(ans, abs(sum - 2 * size[u]));
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i], sum += a[i];
    for (int i = 1; i < n; ++i) {
        int u, v;
        cin >> u >> v;
        addEdge(u, v);
        addEdge(v, u);
    }
    // DFS 및 결과 처리 부분 생략
    cout << ans;
    return 0;
}

C: H 군의 문자열

문제의 답이 여러 가지 연산에 대응될 수 있음을 발견했습니다. 경쟁 중에는 가능한 모든 연산을 고려하려고 했지만 성공하지 못했습니다. 대신 가능한 모든 유효한 결과를 카운팅하는 방식으로 접근하였습니다.

D: H 군의 케이크

WQS 이진 탐색을 활용하여 문제를 해결하였습니다.

태그: 세그먼트 트리 dfs 문자열 매칭 WQS 이진 탐색

6월 4일 03:09에 게시됨