스크류바가 코딩하는 블로그
카테고리아카이브태그소개
자동

© 2026 스쿠루. All rights reserved.

개인정보 처리방침RSS

Seagull Population

2026.10.11|2 min read|PS

The 2025 ICPC Asia Yokohama Regional Contest C

ICPC

문제

링크: Seagull Population

풀이

하한

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

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

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

iii번째 날에 도착한 갈매기의 수를 aia_iai​, 떠난 갈매기의 수를 did_idi​라 하면 bi=bi−1+ai−dib_i = b_{i - 1} + a_i - d_ibi​=bi−1​+ai​−di​입니다.

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

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

선형 풀이

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

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

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

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

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

합칠 조건

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

iii번 갈매기가 도착한 날을 sis_isi​, 머무른 마지막 날을 eie_iei​라 합시다.

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

최대 매칭

첫 번째 날부터 iii번째 날까지 도착한 갈매기의 누적 합을 AiA_iAi​, iii번째 날까지 떠난 갈매기의 누적 합을 DiD_iDi​라 합시다.

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

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

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

두 갈매기를 합치려면 ei<sL−k+ie_i < s_{L - k + i}ei​<sL−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+i)−i=L−k=L−min(b1​,bn​,L−max(bi​))≥max(bi​)이므로 두 갈매기는 항상 구간이 겹치지 않습니다.

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

최적 증명

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))L−k=L−min(b1​,bn​,L−max(bi​))=max(L−b1​,L−bn​,max(bi​))=max(max(L−b1​,L−bn​),max(bi​))입니다.

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

mmm의 하한 중 하나인 ∑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})∑max(0,bi​−bi−1​)=max(0,b1​−bn​)+i=2∑n​max(0,bi​−bi−1​)입니다.

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

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

따라서 L−kL - kL−k는 앞서 찾은 하한인 max⁡(max⁡(bi),∑max⁡(0,bi−bi−1))\max(\max(b_i), \sum \max(0, b_i - b_{i - 1}))max(max(bi​),∑max(0,bi​−bi−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;
}

목차

  • 문제
  • 풀이
  • 코드

댓글

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