Seagull Population

The 2025 ICPC Asia Yokohama Regional Contest C

2 min readPS

문제

링크: Seagull Population

풀이

하한

답 mm의 하한을 생각해 봅시다.

먼저 ii번째 날에는 적어도 bib_i마리가 있어야 하므로 m≥max⁡(bi)m \geq \max(b_i)입니다.

문제가 원형이므로 b0=bnb_0 = b_n으로 생각합시다.

ii번째 날에 도착한 갈매기의 수를 aia_i, 떠난 갈매기의 수를 did_i라 하면 bi=bi−1+ai−dib_i = b_{i - 1} + a_i - d_i입니다.

ai=bi−bi−1+dia_i = b_i - b_{i - 1} + d_i이므로 ai≥bi−bi−1a_i \geq b_i - b_{i - 1}입니다. aia_i의 정의상 ai≥0a_i \geq 0이므로 ∑ai≥∑max⁡(0,bi−bi−1)\sum a_i \geq \sum \max(0, b_i - b_{i - 1})입니다.

따라서 m≥∑max⁡(0,bi−bi−1)m \geq \sum \max(0, b_i - b_{i - 1})입니다.

선형 풀이

만약 문제가 원형이 아니라면 큐를 이용해서 문제를 해결할 수 있습니다.

i=1i = 1부터 nn까지 반복합니다. 여기서는 문제가 선형이므로 b0=0b_0 = 0으로 생각합시다.

  • bi>bi−1b_i > b_{i - 1}이면 ii를 bi−bi−1b_i - b_{i - 1}만큼 큐에 넣습니다.
  • bi<bi−1b_i < b_{i - 1}이면 큐에서 bi−1−bib_{i - 1} - b_i개를 꺼내서 각각 끝점을 i−1i - 1로 기록하고 출력합니다.

해당 방법으로 풀었을 때의 답을 LL이라 합시다. L=∑max⁡(0,bi−bi−1)L = \sum \max(0, b_i - b_{i - 1})입니다. 원형과 달리 b0=0b_0 = 0임에 주의합시다.

위 알고리즘에서 구간의 시작이 11인 것과 구간의 끝이 nn인 것을 최대한 합치면 답이 됩니다.

합칠 조건

큐에 넣은 순서대로 갈매기에게 번호를 매겨봅시다.

ii번 갈매기가 도착한 날을 sis_i, 머무른 마지막 날을 eie_i라 합시다.

si=1s_i = 1인 갈매기와 ej=ne_j = n인 갈매기를 합치려면 ei<sje_i < s_j여야 합니다.

최대 매칭

첫 번째 날부터 ii번째 날까지 도착한 갈매기의 누적 합을 AiA_i, ii번째 날까지 떠난 갈매기의 누적 합을 DiD_i라 합시다.

ii번째 날 섬에 있는 갈매기들은 Di−1+1D_{i - 1} + 1부터 AiA_i까지의 연속된 번호를 갖습니다. 이 구간의 크기는 정확히 bib_i이므로 어떤 두 갈매기의 번호가 max⁡(bi)\max(b_i) 이상 차이 난다면 그 두 갈매기는 겹치지 않으므로 합칠 수 있습니다.

이 성질에 의해 k=min⁡(b1,bn,L−max⁡(bi))k = \min(b_1, b_n, L - \max(b_i))개를 서로 합칠 수 있습니다.

i=1i = 1부터 kk까지 ii번 갈매기와 L−k+iL - k + i번 갈매기를 서로 합쳐봅시다.

두 갈매기를 합치려면 ei<sL−k+ie_i < s_{L - k + i}여야 합니다.

그런데 (L−k+i)−i=L−k=L−min⁡(b1,bn,L−max⁡(bi))≥max⁡(bi)(L - k + i) - i = L - k = L - \min(b_1, b_n, L - \max(b_i)) \geq \max(b_i)이므로 두 갈매기는 항상 구간이 겹치지 않습니다.

따라서 두 갈매기를 합칠 수 있습니다.

최적 증명

L−k=L−min⁡(b1,bn,L−max⁡(bi))=max⁡(L−b1,L−bn,max⁡(bi))=max⁡(max⁡(L−b1,L−bn),max⁡(bi))L - k = L - \min(b_1, b_n, L - \max(b_i)) = \max(L - b_1, L - b_n, \max(b_i)) = \max(\max(L - b_1, L - b_n), \max(b_i))입니다.

원형 기준으로 b0=bnb_0 = b_n이라 두면 L=b1+∑i=2nmax⁡(0,bi−bi−1)L = b_1 + \displaystyle\sum_{i = 2}^n \max(0, b_i - b_{i - 1})입니다.

mm의 하한 중 하나인 ∑max⁡(0,bi−bi−1)=max⁡(0,b1−bn)+∑i=2nmax⁡(0,bi−bi−1)\displaystyle\sum \max(0, b_i - b_{i - 1}) = \max(0, b_1 - b_n) + \displaystyle\sum_{i = 2}^n \max(0, b_i - b_{i - 1})입니다.

만약 b1≥bnb_1 \geq b_n이라면 ∑max⁡(0,bi−bi−1)=L−bn\displaystyle\sum \max(0, b_i - b_{i - 1}) = L - b_n이 됩니다.

만약 b1<bnb_1 < b_n이라면 ∑max⁡(0,bi−bi−1)=L−b1\displaystyle\sum \max(0, b_i - b_{i - 1}) = L - b_1이 됩니다.

따라서 L−kL - k는 앞서 찾은 하한인 max⁡(max⁡(bi),∑max⁡(0,bi−bi−1))\max(\max(b_i), \sum \max(0, b_i - b_{i - 1}))와 같으므로 최적입니다.

코드

#include <bits/stdc++.h>
using namespace std;
 
using ll = long long;
 
constexpr int LIMIT = 2e5;
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int n;
    cin >> n;
 
    vector<ll> b(n + 2);
    for (int i = 1; i <= n; i++) cin >> b[i];
 
    ll M = b[1];
    ll D = max(0LL, b[1] - b[n]);
    for (int i = 2; i <= n; i++) {
        M = max(M, b[i]);
        D += max(0LL, b[i] - b[i - 1]);
    }
 
    ll m = max(M, D);
    cout << m << '\n';
    if (m > LIMIT) return 0;
 
    queue<int> entry;
    vector<pair<int, int>> ans = {{0, 0}};
    for (int i = 1; i <= n + 1; i++) {
        if (b[i] > b[i - 1]) {
            for (int j = 0; j < b[i] - b[i - 1]; j++) entry.push(i);
        } else {
            for (int j = 0; j < b[i - 1] - b[i]; j++) {
                int f = entry.front();
                entry.pop();
 
                ans.emplace_back(f, i - 1);
            }
        }
    }
 
    int L = ans.size() - 1;
    int k = min({b[1], b[n], L - M});
 
    for (int i = k; i >= 1; i--) {
        ans[i].first = ans.back().first;
        ans.pop_back();
    }
 
    for (int i = 1; i <= L - k; i++) {
        auto [s, t] = ans[i];
        cout << s << ' ' << t << '\n';
    }
 
    return 0;
}

댓글

이름과 이메일을 입력해 댓글을 남겨주세요.