:NOIP 시뮬레이션 경진대회 문제 풀이

T1 다채로운 색상

문제는 다음과 같습니다:

  • nxm 크기의 행렬이 주어집니다.
  • (i,j) 위치에는 색깔 ci,j가 있습니다.
  • 네 모서리의 색상이 모두 동일하지 않은 모든 하위 행렬의 수를 구하세요.

시간 복잡도 O(n²m)으로 해결할 수 있습니다. 두 행을 선택한 뒤 열을 스캔하면서 해당 열의 값들이 같으면 답에 기여할 가능성이 있습니다. 이를 위해 카운트 배열을 사용하여 현재 등장 횟수를 추적합니다.


#include <iostream>
#include <vector>

using namespace std;

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

    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> grid(n + 1, vector<int>(m + 1));
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> grid[i][j];
        }
    }

    long long total = 0;
    vector<long long> freq(1000001, 0);

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= i; ++j) {
            fill(freq.begin(), freq.end(), 0); // Reset frequency
            for (int k = 1; k <= m; ++k) {
                if (grid[i][k] == grid[j][k]) {
                    total += freq[grid[i][k]];
                    freq[grid[i][k]]++;
                }
            }
        }
    }

    cout << (long long)n * m * (n + 1) * (m + 1) / 4 - total;
}

T2 피크 타임 여행

문제 요약은 다음과 같습니다:

  • n개의 정점과 완전 그래프가 주어집니다.
  • 각 시간 t에서 특정 정점들만 방문할 수 있습니다.
  • S부터 T까지 가능한 경로 수를 계산하세요.

각 시간마다 유효한 정점의 개수를 곱하는 방식으로 해결할 수 있습니다. 이때 연속된 구간에서는 동일한 값을 가지므로, 효율적으로 계산하기 위해 스캐닝 및 지수 함수를 활용합니다.


#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int MOD = 1e9 + 7;

int power(int base, int exp) {
    int res = 1;
    while (exp > 0) {
        if (exp % 2 == 1) res = (long long)res * base % MOD;
        base = (long long)base * base % MOD;
        exp /= 2;
    }
    return res;
}

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

    int n, m, S, T;
    cin >> n >> m >> S >> T;

    struct Event { int time, delta; };
    vector<Event> events;

    for (int i = 0; i < m; ++i) {
        int x, l, r;
        cin >> x >> l >> r;
        events.push_back({l, -1});
        events.push_back({r + 1, 1});
    }

    events.push_back({S, 0});
    events.push_back({T + 1, 0});

    sort(events.begin(), events.end(), [](const Event &a, const Event &b) { return a.time < b.time; });

    int current = n;
    long long result = 1;
    for (size_t i = 1; i < events.size(); ++i) {
        int duration = events[i].time - events[i - 1].time;
        if (duration > 0) {
            result = (result * power(current, duration)) % MOD;
        }
        current += events[i].delta;
    }

    cout << result;
}

T3 최적화된 세그먼트 트리

문제 설명은 다음과 같습니다:

  • 길이 n의 세그먼트 트리를 구성합니다.
  • q개의 쿼리에 대해 각 쿼리의 처리 비용을 최소화하세요.

구간 DP로 접근할 수 있습니다. 주요 아이디어는 특정 위치에서 나누었을 때 영향을 받는 쿼리들의 수를 사전에 계산하여 저장하는 것입니다.


#include <iostream>
#include <vector>

using namespace std;

const long long INF = 1e18;

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

    int n, q;
    cin >> n >> q;

    vector<pair<int, int>> queries(q);
    for (auto &[l, r] : queries) cin >> l >> r;

    vector<int> w(n + 1, 0);
    vector<vector<int>> num(n + 1, vector<int>(n + 1, 0));

    for (auto &[l, r] : queries) {
        w[l]++;
        w[r]--;
        num[l][r]++;
    }

    for (int i = 1; i <= n; ++i) w[i] += w[i - 1];

    for (int len = 1; len <= n; ++len) {
        for (int l = 1, r = l + len - 1; r <= n; ++l, ++r) {
            num[l][r] += num[l - 1][r] + num[l][r + 1] - num[l - 1][r + 1];
        }
    }

    vector<vector<long long>> dp(n + 1, vector<long long>(n + 1, INF));

    for (int len = 1; len <= n; ++len) {
        for (int l = 1, r = l + len - 1; r <= n; ++l, ++r) {
            if (len == 1) {
                dp[l][r] = 0;
                continue;
            }
            for (int k = l; k < r; ++k) {
                dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + w[k] - num[l][r]);
            }
        }
    }

    cout << dp[1][n] + q;
}

태그: NOIP DP algorithm

6월 17일 19:19에 게시됨