포스트

PS 일기 2024년 3월 6일 (4일 차) (13557번 수열과 쿼리 10)

PS 일기 2024년 3월 6일 (4일 차) (13557번 수열과 쿼리 10)

PS 일기 2024년 2월 8일 (3일 차)

PS 일기란?

PS 실력 향상을 목적으로 날마다 푼 문제를 회고하는 것입니다. 문제의 간단한 해법과 함께 어떤 방식으로 접근했는지, 그리고 문제의 해법을 찾는 데 결정적이었던 깨달음은 무엇이었는지 등을 기록합니다.

오늘의 문제

13557번 수열과 쿼리 10

알고리즘 분류

  • Segment Tree
  • 누적합

문제 풀이

$P_i = A_i + P_{i-1} (1 \leq i \leq N)$

을 누적합이라 할 때

$\sum_{k = s}^{e} A_k = P_e - P_{s-1}$

입니다. $P_e - P_{s-1}$를 최대화하기 위해선 $P_e$가 가장 큰 값, $P_{s-1}$가 가장 작은 값 이어야 합니다. 이를 식으로 표현하면 이와 같습니다.

$\max_{k = x2}^{y2}P_k - \min_{k = x1-1}^{y1-1}P_k$

s값은 -1 한 상태의 min값을 구해야 하기 때문에 $[x1-1, y1-1]$구간에서 min값을 구해야 합니다. 그래서 세그먼트 트리로 min 값과 max값을 관리하면 $(x1, y1)$와 $(x2, y2)$가 겹치지 않은 경우를 계산할 수 있습니다.

만약 $(x1, y1)$과 $(x2, y2)$가 겹친다면($x2 \leq y1$라면) 3가지의 case로 나누어 답을 계산할 수 있습니다.

  1. $x1 \leq s \leq y1 \leq e \leq y2$ 인 경우 $[x1, y1]$구간에서 min값을 구하고 $[y1, y2]$구간에서 max값을 구해 답을 구합니다.
  2. $x1 \leq s \leq x2 \leq e \leq y2$ 인 경우 1. 의 경우와 비슷하게 $[x1, x2]$구간에서 min값을 구하고 $[x2, y2]$구간에서 max값을 구해 답을 구합니다.
  3. $x2 \leq s \leq e \leq y1$ 인 경우 세그먼트 트리에서 두 자식 노드를 합칠 때 (merge 함수) 왼쪽 자식의 min 값과 오른쪽 자식의 max 값의 차이, 왼쪽 자식의 max diff와 오른쪽 자식의 max diff 중 가장 큰 값을 선택합니다.

구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const ll MAXN = 100005;
const ll INF = 1e18;

struct T {
    ll mn, mx;
    ll mx_diff;
};

ll N, M, arr[MAXN], psum[MAXN];
T tree[4*MAXN];

T merge(T l, T r)
{
    T ret;
    ret.mn = min(l.mn, r.mn);
    ret.mx = max(l.mx, r.mx);
    ret.mx_diff = max({l.mx_diff, r.mx_diff, r.mx - l.mn});
    return ret;
}

void init(ll s = 0, ll e = N, ll idx = 1)
{
    if(s == e) {
        tree[idx].mn = tree[idx].mx = psum[s];
        tree[idx].mx_diff = -INF;
        return;
    }
    ll m = (s + e) >> 1;
    init(s, m, 2*idx); init(m+1, e, 2*idx+1);
    tree[idx] = merge(tree[2*idx], tree[2*idx+1]);
}

T query(ll l, ll r, ll s = 0, ll e = N, ll idx = 1)
{
    if(e < l || r < s) return T{INF, -INF, -INF};
    if(l <= s && e <= r) return tree[idx];
    ll m = (s + e) >> 1;
    return merge(query(l, r, s, m, 2*idx), query(l, r, m+1, e, 2*idx+1));
}

int main()
{
    ios_base::sync_with_stdio(false); cin.tie(NULL);

    cin >> N;
    for(ll i = 1; i <= N; i++) {
        cin >> arr[i];
    }

    for(ll i = 1; i <= N; i++) {
        psum[i] = arr[i] + psum[i-1];
    }
    init();
    cin >> M;
    for(ll i = 0; i < M; i++) {
        ll x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        ll ans = -INF;
        if(x2 <= y1) {
            T t = query(x2 - 1, y1);
            ans = max(ans, t.mx_diff);

            T l = query(x1 - 1, y1 - 1);
            T r = query(y1, y2);
            ans = max(ans, r.mx - l.mn);

            l = query(x1 - 1, x2 - 1);
            r = query(x2, y2);
            ans = max(ans, r.mx - l.mn);
        } else {
            T l = query(x1 - 1, y1 - 1);
            T r = query(x2, y2);
            ans = max(r.mx - l.mn, ans);
        }
        cout << ans << "\n";
    }
}

후기

이 글을 보는 사람이 있을까?

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.