2018년 제9회 복건성 대학생 프로그래밍 경시대회 팀 연습 문제 분석

A. 기호 기반 계산 시스템 구현

주어진 명령어 집합(정의, 곱셈, 나눗셈, 덧셈, 뺄셈, 모듈러 연산)을 기반으로 간단한 식 계산 시스템을 구현한다. 각 명령어는 변수에 대한 연산을 수행하며, 결과는 항상 모듈러 연산을 통해 유지된다.

#include <set>
#include <map>
#include <iostream>
using namespace std;

typedef long long LL;
const LL MOD = 1LL << 47;

LL fast_multiply(LL a, LL b) {
    LL result = 0;
    while (b) {
        if (b & 1) result = (result + a) % MOD;
        a = (a * 2) % MOD;
        b >>= 1;
    }
    return result % MOD;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    string command, var1, var2;
    map<string, LL> variables;

    while (cin >> command) {
        if (command == "def") {
            cin >> var1 >> var2;
            variables[var1] = stoll(var2) % MOD;
            cout << var1 << " = " << variables[var1] << '\n';
        } else if (command == "add") {
            cin >> var1 >> var2;
            variables[var1] = (variables[var1] + variables[var2]) % MOD;
            cout << var1 << " = " << variables[var1] << '\n';
        } else if (command == "sub") {
            cin >> var1 >> var2;
            variables[var1] = (variables[var1] - variables[var2] + MOD) % MOD;
            cout << var1 << " = " << variables[var1] << '\n';
        } else if (command == "mul") {
            cin >> var1 >> var2;
            variables[var1] = fast_multiply(variables[var1], variables[var2]) % MOD;
            cout << var1 << " = " << variables[var1] << '\n';
        } else if (command == "div") {
            cin >> var1 >> var2;
            variables[var1] = (variables[var1] / variables[var2] + MOD) % MOD;
            cout << var1 << " = " << variables[var1] << '\n';
        } else if (command == "mod") {
            cin >> var1 >> var2;
            variables[var1] = variables[var1] % variables[var2];
            cout << var1 << " = " << variables[var1] << '\n';
        }
    }

    return 0;
}

D. 동적 조건 하에서의 누적 곱 계산

각 단계에서 값을 곱하거나 특정 이전 단계의 값을 무시하는 방식으로 진행되는 연산 시뮬레이션. 선형 시간 내에 모든 상태를 관리하기 위해 세그먼트 트리를 활용하여 구간 곱을 효율적으로 계산한다.

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

typedef long long LL;
const int MAXN = 1e5 + 5;
const int MOD = 1e9 + 7;

struct SegmentTree {
    int l, r;
    LL mul;
} tree[MAXN * 4];

void push_up(int rt) {
    tree[rt].mul = (tree[rt << 1].mul * tree[rt << 1 | 1].mul) % MOD;
}

void build(int rt, int left, int right) {
    tree[rt].l = left;
    tree[rt].r = right;
    tree[rt].mul = 1;
    if (left == right) return;
    int mid = (left + right) / 2;
    build(rt << 1, left, mid);
    build(rt << 1 | 1, mid + 1, right);
    push_up(rt);
}

void update(int rt, int pos, int val) {
    if (tree[rt].l == pos && tree[rt].r == pos) {
        tree[rt].mul = val;
        return;
    }
    int mid = (tree[rt].l + tree[rt].r) / 2;
    if (pos <= mid) update(rt << 1, pos, val);
    else update(rt << 1 | 1, pos, val);
    push_up(rt);
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int Q, M;
        scanf("%d%d", &Q, &M);
        build(1, 1, Q);
        char op[2];
        int x;
        for (int i = 1; i <= Q; ++i) {
            scanf("%s%d", op, &x);
            if (op[0] == 'M') {
                update(1, i, x);
            } else {
                update(1, x, 1);
            }
            printf("%lld\n", tree[1].mul);
        }
    }
    return 0;
}

E. 제약 조건이 있는 최단 경로 탐색

노드마다 이동 가능한 시간 구간이 정해져 있으며, 그 구간 내에서만 이동 가능하다. 이를 고려하여 다익스트라 알고리즘을 확장하여, 도착 시점이 해당 노드의 주기 구간 내에서 짝수 또는 홀수 배인지 판단하고, 필요시 지연을 추가한다.

#include <vector>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;

typedef long long LL;
const int MAXN = 1005;
const LL INF = 1e18;

struct Edge {
    int to;
    LL weight;
};

vector<Edge> graph[MAXN];
LL time_limit[MAXN];
LL dist[MAXN];
bool visited[MAXN];

void dijkstra(int start, int end_node) {
    priority_queue<pair<LL, int>, vector<pair<LL, int>>, greater<pair<LL, int>>> pq;
    memset(dist, INF, sizeof(dist));
    memset(visited, false, sizeof(visited));
    dist[start] = 0;
    pq.push({0, start});

    while (!pq.empty()) {
        int u = pq.top().second;
        pq.pop();
        if (visited[u]) continue;
        visited[u] = true;

        for (auto &e : graph[u]) {
            int v = e.to;
            LL cost = dist[u] + e.weight;
            if (time_limit[v] > 0 && v != end_node) {
                LL k = cost / time_limit[v];
                if (k % 2 == 1) cost = (k + 1) * time_limit[v];
            }
            if (cost < dist[v]) {
                dist[v] = cost;
                pq.push({cost, v});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    while (T--) {
        int N, M;
        cin >> N >> M;
        for (int i = 1; i <= N; ++i) {
            cin >> time_limit[i];
        }
        for (int i = 0; i < M; ++i) {
            int u, v;
            LL w;
            cin >> u >> v >> w;
            graph[u].push_back({v, w});
            graph[v].push_back({u, w});
        }
        int S, E;
        cin >> S >> E;
        dijkstra(S, E);
        cout << dist[E] << '\n';
    }

    return 0;
}

G. 두 사각형의 면적 교차 및 합 계산

두 사각형의 좌표와 크기를 입력받아, 면적의 교차 부분과 합 부분을 계산하고, 교차 비율을 출력한다. 범위 확인 후, 수학적 공식을 사용하여 정확한 비율을 도출.

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

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int x1, y1, w1, h1;
        int x2, y2, w2, h2;
        scanf("%d%d%d%d", &x1, &y1, &w1, &h1);
        scanf("%d%d%d%d", &x2, &y2, &w2, &h2);

        LL area1 = (LL)w1 * h1;
        LL area2 = (LL)w2 * h2;
        LL total_area = area1 + area2;

        int overlap_x = max(0, min(x1 + w1, x2 + w2) - max(x1, x2));
        int overlap_y = max(0, min(y1 + h1, y2 + h2) - max(y1, y2));
        LL intersection = (LL)overlap_x * overlap_y;

        double ratio = 1.0 * intersection / (total_area - intersection);
        printf("%.2f\n", ratio);
    }
    return 0;
}

H. 랜덤 피해 분배 확률 계산

총 피해량이 주어졌을 때, 적의 보조 유닛에 최소한의 피해가 들어가도록 하는 경우의 수를 이항 계수를 이용해 계산. 전체 가능성은 2^N이며, 보조 유닛을 죽이기 위한 최소 피해량 조건을 만족하는 경우의 수를 전처리하여 빠르게 계산.

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

typedef long long LL;
const int MOD = 1e9 + 7;
const int MAXN = 1005;

LL fact[MAXN], inv_fact[MAXN];
LL pow2[MAXN], inv_pow2[MAXN];

LL mod_pow(LL base, LL exp) {
    LL res = 1;
    while (exp) {
        if (exp & 1) res = res * base % MOD;
        base = base * base % MOD;
        exp >>= 1;
    }
    return res;
}

void precompute() {
    fact[0] = 1;
    for (int i = 1; i < MAXN; ++i)
        fact[i] = fact[i-1] * i % MOD;
    inv_fact[MAXN-1] = mod_pow(fact[MAXN-1], MOD-2);
    for (int i = MAXN-2; i >= 0; --i)
        inv_fact[i] = inv_fact[i+1] * (i+1) % MOD;
    for (int i = 0; i < MAXN; ++i) {
        pow2[i] = mod_pow(2, i);
        inv_pow2[i] = mod_pow(pow2[i], MOD-2);
    }
}

LL combination(int n, int r) {
    if (r < 0 || r > n) return 0;
    return fact[n] * inv_fact[r] % MOD * inv_fact[n-r] % MOD;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    precompute();

    int T;
    cin >> T;
    while (T--) {
        int n, m;
        cin >> n >> m;
        LL total = pow2[n];
        LL sum = 0;
        for (int i = m; i <= n; ++i)
            sum = (sum + combination(n, i)) % MOD;
        LL ans = (total - sum + MOD) % MOD;
        ans = ans * inv_pow2[n] % MOD;
        cout << ans << '\n';
    }
    return 0;
}

J. 순서 기반 신뢰도 확률 계산

사람들이 일렬로 연결되어 있으며, 한 사람에게 과자를 주면 자신과 그 아래 계층의 모든 사람이 진실을 말하게 된다. 과자 수가 주어졌을 때, 진실을 말하는 사람 수의 기대값을 이론적으로 계산. 조합 수의 성질을 활용해 간단한 공식으로 해결.

#include <cstdio>
using namespace std;

typedef long long LL;
const int MOD = 1e9 + 7;

LL mod_pow(LL base, LL exp) {
    LL res = 1;
    while (exp) {
        if (exp & 1) res = res * base % MOD;
        base = base * base % MOD;
        exp >>= 1;
    }
    return res;
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        LL n, m;
        scanf("%lld%lld", &n, &m);
        if (m >= n) {
            printf("%lld\n", n);
            continue;
        }
        LL inv_m_plus_1 = mod_pow(m + 1, MOD - 2);
        LL result = (m * (n + 1) % MOD) * inv_m_plus_1 % MOD;
        printf("%lld\n", result);
    }
    return 0;
}

태그: 세그먼트 트리 다익스트라 알고리즘 이항 계수 조합 수 확률 계산

6월 1일 01:20에 게시됨