https://www.acmicpc.net/problem/2912

 

2912번: 백설공주와 난쟁이

첫째 줄에 난쟁이의 수 N과 모자 색상의 수 C가 주어진다. (3 ≤ N ≤ 300,000, 1 ≤ C ≤ 10,000) 둘째 줄에는 각 난쟁이가 쓰고 있는 모자의 색상이 줄을 서 있는 순서대로 주어진다. 셋째 줄에는 사진

www.acmicpc.net

N명의 난쟁이가 모자를 쓰고 줄을 서고 있습니다.

구간 l ~ r에 존재하는 모자의 색상의 갯수를 종류별로 파악하여 어떤 모자가 절반보다 많이 있다면 yes와 색의 번호를 출력합니다.

 

이 문제에 업데이트가 없으므로 오프라인 쿼리가 가능하며, 구간을 효율적으로 움직이기 위해 Mo's 알고리즘을 사용합니다.

먼저 구간을 sqrt(N)으로 나누어 l / sqrt(N)을 기준으로 오름차순 정렬합니다.

l의 구간이 같다면 r을 기준으로 오름차순 정렬합니다.

 

그 다음 정렬된 쿼리를 하나씩 처리합니다.

구간을 이동하면서 색에 대한 카운팅을 진행합니다.

해당 구간에 대한 카운팅이 끝났으면 어떤 색이 절반보다 많이 차지하는지 찾습니다. (없다면 -1입니다.)

그 다음 구간을 갱신합니다.

M개의 쿼리를 모두 처리한 다음 한꺼번에 출력합니다.

 

 

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
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <algorithm>
#include <cmath>
#define MAX 300001
#define MAXQ 10001
using namespace std;
 
typedef struct Point {
    int l, r, idx;
}Point;
 
Point q[MAXQ];
int list[MAX], ans[MAXQ], cnt[MAXQ];
int N, C, M, sqrtN;
 
bool cmp(Point a, Point b) {
    int x = a.l / sqrtN;
    int y = b.l / sqrtN;
 
    if (x == y) return a.r < b.r;
    else return x < y;
}
 
void solve(int idx, int k) {
    for (int i = 1; i <= C; i++) {
        if (cnt[i] > k / 2) {
            ans[idx] = i;
            return;
        }
    }
    ans[idx] = -1;
}
 
void func() {
    int l = q[1].l;
    int r = q[1].r;
    for (int i = l; i <= r; i++) {
        cnt[list[i]]++;
    }
    solve(q[1].idx, r - l + 1);
 
    for (int i = 2; i <= M; i++) {
        int nl = q[i].l;
        int nr = q[i].r;
        int idx = q[i].idx;
 
        for (int j = l; j < nl; j++) {
            cnt[list[j]]--;
        }
        for (int j = l - 1; j >= nl; j--) {
            cnt[list[j]]++;
        }
        for (int j = r; j > nr; j--) {
            cnt[list[j]]--;
        }
        for (int j = r + 1; j <= nr; j++) {
            cnt[list[j]]++;
        }
        solve(idx, nr - nl + 1);
 
        l = nl;
        r = nr;
    }
 
    for (int i = 1; i <= M; i++) {
        if (ans[i] == -1cout << "no\n";
        else cout << "yes " << ans[i] << '\n';
    }
}
 
void input() {
    cin >> N >> C;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
    sqrtN = sqrt(N);
 
    cin >> M;
    for (int i = 1; i <= M; i++) {
        cin >> q[i].l >> q[i].r;
        q[i].idx = i;
    }
    sort(q + 1, q + 1 + M, cmp);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > Mo's' 카테고리의 다른 글

boj 12999 화려한 마을3  (0) 2022.09.07

https://www.acmicpc.net/problem/12999

 

12999번: 화려한 마을3

첫 번째 줄에 N, Q (1 ≤ N ≤ 100,000, 1 ≤ Q ≤ 100,000)이 공백을 구분으로 주어진다. 각각 천나라에 존재하는 집의 수와 민호가 궁금해 하는 특정 구간의 수이다. 두 번째 줄에는 1번 집부터 N번 집까

www.acmicpc.net

구간 l ~ r에 존재하는 집에 칠해진 페인트의 밝기들 중 가장 많은 것의 갯수를 출력합니다.

즉, [l, r] 구간에 {1, 1, 1, 2, 2}가 있다면 1이 3개, 2가 2개이므로 3을 출력합니다.

 

이 문제에서 사용할 배열은

  • q: 쿼리 정보를 저장하는 배열
  • list: 페인트의 밝기를 저장하는 배열
  • ans: 답을 저장하는 배열
  • cnt: 특정 밝기의 등장 횟수
  • cntCnt: 특정 밝기의 등장 횟수의 등장 횟수

여기서 cntCnt는 1이 3번, 2가 3번 등장했다면 cntCnt[3] = 2 이렇게 되는 방식입니다.

페인트의 밝기는 -100000 ~ 100000이므로 100000을 미리 더해주도록 합시다. 그래서 cnt 배열의 크기는 *2가 됩니다.

 

페인트의 밝기는 업데이트가 되지 않으므로 오프라인 쿼리 사용이 가능합니다.

따라서 쿼리 정보를 한 번에 받아서 배열 q에 저장합니다.

출력은 입력 순으로 해야하니 인덱스도 함께 저장해야 합니다.

 

그 다음, 투 포인터 비슷하게 l ~ r 구간을 하나하나 확인하여 갯수 카운팅을 진행합니다.

하지만 범위가 1 ~ 5 -> 9 ~ 10 -> 1 ~ 2 이런식으로 된다면 포인터가 이동하는데 많은 시간이 걸릴 것입니다.

포인터를 효율적으로 이동시키기 위해 Mo's 알고리즘을 사용합니다.

Mo's 알고리즘으로 구간을 sqrt(N)으로 나누어서 최대한 같은 그룹 내에서 이동할 수 있도록 하여 움직임을 최소화할 수 있습니다.

따라서 l / sqrt(N)을 기준으로 오름차순으로 정렬하며, 같다면 r 기준으로 오름차순으로 정렬합니다.

 

이제 정렬된 쿼리를 하나씩 처리하면 됩니다.

구간을 이동시키면서 수를 제거하고, 추가합니다.

 

수를 제거하는 과정에서는 먼저 해당 수가 등장한 횟수인 cntCnt를 하나 빼야합니다.

1이 3번 등장해서 cntCnt[3] = 1이 되었는데, 구간을 이동시키니 1이 제거돼야 하는 상황이라면 1이 2번 등장하는 것으로 바뀌게 됩니다.

따라서 cntCnt[3]에서 1을 빼고, cntCnt[2]에서 1을 더해야 합니다.

이 때, 만약 cntCnt[3]이 0이 되었고, 그게 현재 최대 등장 횟수 ret이었다면 ret 또한 1을 빼야합니다.

 

수를 추가하는 과정에서는 해당 수가 처음 등장하는 수가 아니라면 cntCnt를 하나 빼줍니다.

1이 3번 등장해서 cntCnt[3] = 1이 되었고, 구간을 이동시켜서 1이 추가되는 상황이라면 1이 4번 등장하는 것으로 바뀌게 됩니다.

따라서 cntCnt[3]에서 1을 빼고, cntCnt[4]에서 1을 더해야 합니다.

물론, 1이 등장한 적 없다가 추가되는 상황이면 cntCnt를 빼지 않아야 합니다.

그리고 등장 횟수를 추가하는 과정이므로 최대 등장 횟수 ret을 계속 갱신합니다.

 

위 두 가지 작업이 완료되었다면 구간을 갱신하고, 쿼리의 인덱스에 맞게 정답을 넣어줍니다.

쿼리가 입력 순으로 정렬되어 있지 않으므로 출력은 마지막에 한꺼번에 진행합니다.

 

https://www.acmicpc.net/problem/12986

 

12986번: 화려한 마을2

첫 번째 줄에 N, Q (1 ≤ N ≤ 100,000, 1 ≤ Q ≤ 100,000)이 공백을 구분으로 주어진다. 각각 천나라에 존재하는 집의 수와 민호가 궁금해 하는 특정 구간의 수이다. 두 번째 줄에는 1번 집부터 N번 집까

www.acmicpc.net

이 문제와 같은 풀이입니다.

하지만 화려한 마을2 문제는 다른 조건이 있어 다른 풀이로도 풀 수 있습니다.

저는 화려한 마을3을 먼저 풀었기에 같은 풀이로 AC를 받았습니다.

 

 

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <algorithm>
#include <cmath>
#define MAX 100001
using namespace std;
 
typedef struct Point {
    int l, r, idx;
}Point;
 
Point q[MAX];
int list[MAX], ans[MAX], cnt[MAX * 2], cntCnt[MAX];
int N, M, sqrtN;
 
bool cmp(Point a, Point b) {
    int x = a.l / sqrtN;
    int y = b.l / sqrtN;
    if (x == y) return a.r < b.r;
    else return x < y;
}
 
void func() {
    int l = q[1].l;
    int r = q[1].r;
    int ret = 0;
    for (int i = l; i <= r; i++) {
        int x = list[i] + 100000;
        if (cnt[x]) cntCnt[cnt[x]]--;
        cnt[x]++;
        cntCnt[cnt[x]]++;
        ret = max(ret, cnt[x]);
    }
    ans[q[1].idx] = ret;
 
    for (int i = 2; i <= M; i++) {
        int nl = q[i].l;
        int nr = q[i].r;
        int idx = q[i].idx;
 
        for (int j = l; j < nl; j++) {
            int x = list[j] + 100000;
            cntCnt[cnt[x]]--;
            if (!cntCnt[cnt[x]] && ret == cnt[x]) ret--;
            cnt[x]--;
            cntCnt[cnt[x]]++;
        }
        for (int j = l - 1; j >= nl; j--) {
            int x = list[j] + 100000;
            if (cnt[x]) cntCnt[cnt[x]]--;
            cnt[x]++;
            cntCnt[cnt[x]]++;
            ret = max(ret, cnt[x]);
        }
        for (int j = r; j > nr; j--) {
            int x = list[j] + 100000;
            cntCnt[cnt[x]]--;
            if (!cntCnt[cnt[x]] && ret == cnt[x]) ret--;
            cnt[x]--;
            cntCnt[cnt[x]]++;
        }
        for (int j = r + 1; j <= nr; j++) {
            int x = list[j] + 100000;
            if (cnt[x]) cntCnt[cnt[x]]--;
            cnt[x]++;
            cntCnt[cnt[x]]++;
            ret = max(ret, cnt[x]);
        }
 
        l = nl;
        r = nr;
        ans[idx] = ret;
    }
 
    for (int i = 1; i <= M; i++) {
        cout << ans[i] << '\n';
    }
}
 
void input() {
    cin >> N >> M;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
    sqrtN = sqrt(N);
 
    for (int i = 1; i <= M; i++) {
        cin >> q[i].l >> q[i].r;
        q[i].idx = i;
    }
    sort(q + 1, q + 1 + M, cmp);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > Mo's' 카테고리의 다른 글

boj 2912 백설공주와 난쟁이  (0) 2022.09.08

https://www.acmicpc.net/problem/12895

 

12895번: 화려한 마을

첫 번째 줄에 N, T, Q (1 ≤ N ≤ 100,000, 1 ≤ T ≤ 30, 1 ≤ Q ≤ 100,000)이 공백을 구분으로 주어진다. 각각 천나라에 존재하는 집의 개수, 사용할 색의 개수, 작업의 개수를 의미한다. 두 번째 줄부터 작

www.acmicpc.net

출력은 l ~ r 구간에 칠해져 있는 색의 가짓수, 집에 칠할 수 있는 색은 최대 30가지입니다.

업데이트와 쿼리가 섞여 있으므로 오프라인 쿼리 및 mo's 알고리즘은 사용하지 못하고, 세그먼트 트리 lazy를 사용합니다.

트리에는 해당 구간에 칠해져 있는 색의 가짓수를 비트 필드로 저장합니다.

 

우선 모든 집에 1이 칠해져 있는 상태로 시작합니다. init 함수에서 초기화를 진행합니다.

type == C면 l ~ r 구간에 업데이트를 진행합니다.

원래 칠해져 있던 색을 지우고, 새로운 색을 채워야 하므로 tree[node]에 해당 색으로 변경해줍니다.

type == Q면 l ~ r 구간에 칠해져 있는 색의 가짓수를 출력합니다.

트리에는 비트 필드를 저장했으니 시프트연산을 하면서 1의 갯수를 출력합니다.

lazyUpdate는 모든 update, query 시 먼저 진행합니다.

 

이 문제는 l > r인 입력도 주어지니 l < r이 되도록 swap을 해야합니다.

 

 

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
84
85
86
87
88
89
90
91
92
#include <iostream>
#define MAX 100001
using namespace std;
 
int tree[MAX * 4], lazy[MAX * 4];
int N, M, K;
 
void init(int node, int s, int e) {
    if (s == e) {
        tree[node] = 1;
        return;
    }
 
    int m = (s + e) / 2;
    init(node * 2, s, m);
    init(node * 2 + 1, m + 1, e);
    tree[node] = tree[node * 2| tree[node * 2 + 1];
}
 
void lazyUpdate(int node, int s, int e) {
    if (!lazy[node]) return;
 
    tree[node] = lazy[node];
    if (s != e) {
        lazy[node * 2= lazy[node];
        lazy[node * 2 + 1= lazy[node];
    }
    lazy[node] = 0;
}
 
void update(int node, int s, int e, int l, int r, int k) {
    lazyUpdate(node, s, e);
    if (s > r || l > e) return;
    if (l <= s && e <= r) {
        lazy[node] = (1 << k);
        lazyUpdate(node, s, e);
        return;
    }
 
    int m = (s + e) / 2;
    update(node * 2, s, m, l, r, k);
    update(node * 2 + 1, m + 1, e, l, r, k);
    tree[node] = tree[node * 2| tree[node * 2 + 1];
}
 
int query(int node, int s, int e, int l, int r) {
    lazyUpdate(node, s, e);
    if (s > r || l > e) return 0;
    if (l <= s && e <= r) return tree[node];
 
    int m = (s + e) / 2;
    return query(node * 2, s, m, l, r) | query(node * 2 + 1, m + 1, e, l, r);
}
 
void func() {
    char type;
    int l, r, k;
    while (K--) {
        cin >> type;
        if (type == 'C') {
            cin >> l >> r >> k;
            if (l > r) swap(l, r);
            update(11, N, l, r, k - 1);
        }
        else {
            cin >> l >> r;
            if (l > r) swap(l, r);
            int ret = query(11, N, l, r);
            int ans = 0;
            while (ret) {
                ans += (ret & 1);
                ret >>= 1;
            }
            cout << ans << '\n';
        }
    }
}
 
void input() {
    cin >> N >> M >> K;
    init(11, N);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > SegmentTree' 카테고리의 다른 글

boj 1517 버블 소트  (0) 2024.06.16
boj 17131 여우가 정보섬에 올라온 이유  (3) 2024.04.25
boj 10167 금광  (0) 2022.09.02
boj 16993 연속합과 쿼리  (0) 2022.08.29
boj 12846 무서운 아르바이트  (0) 2022.08.19

알고리즘

1일 1알고리즘

 

후기

오랜만에 ssafy 5기 1학기 같은반 교육생분들을 만났다.

한 20명? 정도 모였던것 같은데 처음보는 분들이 대부분이어서 신기했다.

사실 1학기때는 민폐만 끼친 것 같고, 좋은 기억이 없었어서 가는게 맞는지 고민을 많이 했었다.

하지만 단체로 모여서 얘기도 많이 나누고 하니 좋은 시간이었던 것 같고, 수업을 오프라인으로 했더라면.. 재미있었겠다 하는 생각도 들었다 ㅠ

다들 어디서 많이 들어본 좋은 곳으로 취업하신걸 보니 대단했다. 물론 나처럼 스타트업에서 역량 키우시는 분도 계셔서 반가웠다. ㅎㅎ

 

역시나 개발자들이 모이니 개발 관련 얘기를 많이 했고, 이번 모임에서 느낀것은 정말 열심히 사는분들이 많다는 것이다.

취업했다고 끝이 아닌, 성장 혹은 경험을 위해 어떤 프로그램에 참여하거나 스터디도 꾸준히 하는 분이 많았고, 자격증을 따시는 분도 많았다.

덕분에 내가 느낄 수 있는 점도 많고, 내 자신을 다시 반성하게 되면서 ㅎㅎㅎ 더 열심히 살아야 겠다는 생각이 든다.

앞으로 이렇게 만날 기회가 거의 없을 것 같은데 이번에라도 만나서 다행이라는 생각이 들었다.

'잡담' 카테고리의 다른 글

9월 3주차 결산  (0) 2022.09.18
9월 2주차 결산  (0) 2022.09.11
8월 4주차 결산  (0) 2022.08.29
8월 3주차 결산  (0) 2022.08.22
8월 2주차 결산  (0) 2022.08.14

https://www.acmicpc.net/problem/10167

 

10167번: 금광

첫 줄에는 금광들의 개수 N (1 ≤ N ≤ 3,000)이 주어진다. 이어지는 N개의 줄 각각에는 금광의 좌표 (x, y)를 나타내는 음이 아닌 두 정수 x와 y(0 ≤ x, y ≤ 109), 그리고 금광을 개발하면 얻게 되는 이

www.acmicpc.net

이 문제를 풀기 전에, [boj 16993 연속합과 쿼리] 문제를 먼저 푸시는 것을 추천드립니다. [풀이]

 

금광을 풀기 위해 금광 세그가 등장하여 많이 알려진 것을 보면 얼마나 어려운 문제인지 예상이 될 것 같습니다.

금광세그 + 좌표압축 + 스위핑으로 많은 테크닉이 이 하나의 문제를 풀기 위해 필요합니다.

 

전체 흐름은

  1. 좌표압축으로 좌표 범위를 줄인 후에
  2. y 좌표를 기준으로 x 좌표들을 모으고
  3. y 좌표 범위를 이동시키며, 연속합의 최대를 구한다.

이렇게 되겠습니다.

 

좌표 압축

좌표를 인덱스로 하여 트리를 구성합니다.

좌표압축 설명은 [여기]에 포스팅되어 있습니다.

x, y 좌표 모두 압축 진행해주시면 됩니다.

y 좌표를 기준으로 x 좌표를 모을 예정이므로 다시 재정렬할 필요는 없습니다.

 

스위핑

먼저, y 좌표를 인덱스로 같은 y 좌표를 가지는 x 좌표를 벡터에 모읍니다.

yList[1] = {1, 2, 3} // y = 1인 x 좌표가 1, 2, 3

그 다음 y 좌표 범위를 이동하면서 최적의 답을 구합니다.

y 좌표의 범위는 1 ~ yCnt, 2 ~ yCnt, ..., y ~ yCnt에 존재하는 x 좌표들 대상입니다.

범위가 고정되었으니 이제 x 좌표들로 연속합의 최대를 구합니다.

 

금광세그

여기부터는 이제 연속합과 쿼리 문제와 동일합니다.

연속합의 최대를 구하기 위해 세그먼트 트리를 이용합니다.\

 

트리에는 아래 값들을 저장하게 됩니다.

  • l: leftNode의 최대
    • max(leftNode.l, leftNode.sum + rightNode.l)
  • r: rightNode의 최대
    • max(rightNode.r, rightNode.sum + leftNode.r)
  • max: 현재 노드의 최대
    • max(leftNode.max, rightNode.max, leftNode.r + rightNode.l)
  • sum: 현재 노드의 전체 합
    • leftNode.sum + rightNode.sum

y 좌표의 범위가 바뀔 때마다 들어오는 x 좌표 값들이 다르므로 트리는 반드시 초기화 해야합니다.

 

 

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#define MAX 3001
using namespace std;
typedef long long ll;
typedef pair<intint> pii;
typedef pair<int, ll> pil;
 
typedef struct Point {
    int x, y;
    ll w;
    int idx;
}Point;
 
typedef struct Node {
    ll l, r, max, sum;
}Node;
 
Point list[MAX];
Node tree[MAX * 4];
vector<pil> yList[MAX];
int N;
 
void update(int node, int s, int e, int idx, ll diff) {
    if (idx < s || idx > e) return;
    if (s == e) {
        tree[node].l += diff;
        tree[node].r += diff;
        tree[node].max += diff;
        tree[node].sum += diff;
        return;
    }
 
    int m = (s + e) / 2;
    update(node * 2, s, m, idx, diff);
    update(node * 2 + 1, m + 1, e, idx, diff);
    tree[node] = { max(tree[node * 2].l, tree[node * 2].sum + tree[node * 2 + 1].l), max(tree[node * 2 + 1].r, tree[node * 2 + 1].sum + tree[node * 2].r), max(max(tree[node * 2].max, tree[node * 2 + 1].max), tree[node * 2].r + tree[node * 2 + 1].l), tree[node * 2].sum + tree[node * 2 + 1].sum };
}
 
Node query(int node, int s, int e, int l, int r) {
    if (s > r || l > e) return { (int)-1e9, (int)-1e9, (int)-1e90 };
    if (l <= s && e <= r) return tree[node];
 
    int m = (s + e) / 2;
    Node leftNode = query(node * 2, s, m, l, r);
    Node rightNode = query(node * 2 + 1, m + 1, e, l, r);
    return { max(leftNode.l, leftNode.sum + rightNode.l), max(rightNode.r, rightNode.sum + leftNode.r), max(max(leftNode.max, rightNode.max), leftNode.r + rightNode.l), leftNode.sum + rightNode.sum };
}
 
void func() {
    sort(list + 1, list + 1 + N, [](Point a, Point b) {
        return a.x < b.x;
    });
 
    int pre = 1e9 + 1;
    int xCnt = 0;
    for (int i = 1; i <= N; i++) {
        if (pre != list[i].x) xCnt++;
        pre = list[i].x;
        list[i].x = xCnt;
    }
 
    sort(list + 1, list + 1 + N, [](Point a, Point b) {
        return a.y < b.y;
    });
 
    pre = 1e9 + 1;
    int yCnt = 0;
    for (int i = 1; i <= N; i++) {
        if (pre != list[i].y) yCnt++;
        pre = list[i].y;
        list[i].y = yCnt;
    }
 
    for (int i = 1; i <= N; i++) {
        yList[list[i].y].push_back({ list[i].x, list[i].w });
    }
 
    ll ans = -1e9 - 1;
    for (int i = 1; i <= yCnt; i++) {
        memset(tree, 0sizeof(tree));
        for (int j = i; j <= yCnt; j++) {
            for (int k = 0; k < yList[j].size(); k++) {
                update(11, N, yList[j][k].first, yList[j][k].second);
            }
            ans = max(ans, query(11, N, 1, N).max);
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list[i].x >> list[i].y >> list[i].w;
        list[i].idx = i;
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > SegmentTree' 카테고리의 다른 글

boj 17131 여우가 정보섬에 올라온 이유  (3) 2024.04.25
boj 12895 화려한 마을  (0) 2022.09.07
boj 16993 연속합과 쿼리  (0) 2022.08.29
boj 12846 무서운 아르바이트  (0) 2022.08.19
boj 1849 순열  (0) 2021.09.08

https://www.acmicpc.net/problem/16993

 

16993번: 연속합과 쿼리

길이가 N인 수열 A1, A2, ..., AN이 주어진다. 이때, 다음 쿼리를 수행하는 프로그램을 작성하시오. i j : Ai, Ai+1, ..., Aj에서 가장 큰 연속합을 출력한다. (1 ≤ i ≤ j ≤ N) 수열의 인덱스는 1부터 시작

www.acmicpc.net

"금광 세그" 라고 불릴 정도의 웰노운 문제지만 어렵습니다. ㅠㅠ

이 문제는 "금광" 문제를 풀기 위한 워밍업 문제이며, 이 문제를 푸셨다면 금광 문제에 도전하시는 것도 좋습니다.

 

금광 세그는 구간에서 가장 큰 연속합을 구하는 문제입니다.

세그먼트 트리를 활용할 수 있으며, 두 개의 노드를 병합했을 때,

[왼쪽 노드의 최대, 오른쪽 노드의 최대, 두 노드의 중간]

이 정도가 답이 될 수 있습니다.

 

이를 구하기 위해 세그먼트 트리에는 다음과 같은 값을 저장합니다.

  • l: 왼쪽 하위노드 (leftNode)의 최대
  • r: 오른쪽 하위노드 (rightNode)의 최대
  • max: 현재 노드의 최대
  • sum: 현재 노드의 전체 합

이 값들을 구하는 방법은

  • l: max(leftNode.l, leftNode.sum + rightNode.l)
  • r: max(rightNode.r, rightNode.sum + leftNode.r)
  • max: max(leftNode.max, rightNode.max, leftNode.r + rightNode.l)
  • sum: leftNode.sum + rightNode.sum

이러면 전체 구간에서의 max를 구할 수 있습니다.

 

해당 식을 알고 있다면 쉽게 풀리는 문제지만, 처음보는 입장에서 이해하기는 어려웠습니다.

비슷한 문제로 update가 추가된 문제와 좌표압축이 추가된 금광 문제도 함께 공유드리고, 마무리하겠습니다.

https://www.acmicpc.net/problem/15561

 

15561번: 구간 합 최대? 2

첫 번째 줄에 정수 N과 Q, U, V가 입력된다. (1 ≤ N, Q ≤ 105,  - 5 ≤ U, V ≤ 5) 두 번째 줄에 정수 K1, K2, ..., KN이 주어진다. (-102 ≤ Ki ≤ 102) 세 번째 줄부터 쿼리가

www.acmicpc.net

https://www.acmicpc.net/problem/10167

 

10167번: 금광

첫 줄에는 금광들의 개수 N (1 ≤ N ≤ 3,000)이 주어진다. 이어지는 N개의 줄 각각에는 금광의 좌표 (x, y)를 나타내는 음이 아닌 두 정수 x와 y(0 ≤ x, y ≤ 109), 그리고 금광을 개발하면 얻게 되는 이

www.acmicpc.net

 

 

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
#include <iostream>
#include <algorithm>
#define MAX 100001
using namespace std;
 
typedef struct Node {
    int l;
    int r;
    int max;
    int sum;
}Node;
 
Node tree[MAX * 4];
int list[MAX];
int N, M;
 
Node init(int node, int s, int e) {
    if (s == e) {
        return tree[node] = { list[s], list[s], list[s], list[s] };
    }
 
    int m = (s + e) / 2;
    Node leftNode = init(node * 2, s, m);
    Node rightNode = init(node * 2 + 1, m + 1, e);
    return tree[node] = { max(leftNode.l, leftNode.sum + rightNode.l), max(rightNode.r, leftNode.r + rightNode.sum), max(max(leftNode.max, rightNode.max), leftNode.r + rightNode.l), leftNode.sum + rightNode.sum };
}
 
Node query(int node, int s, int e, int l, int r) {
    if (s > r || l > e) return { (int)-1e9, (int)-1e9, (int)-1e90 };
    if (l <= s && e <= r) return tree[node];
 
    int m = (s + e) / 2;
    Node leftNode = query(node * 2, s, m, l, r);
    Node rightNode = query(node * 2 + 1, m + 1, e, l, r);
    return { max(leftNode.l, leftNode.sum + rightNode.l), max(rightNode.r, leftNode.r + rightNode.sum), max(max(leftNode.max, rightNode.max), leftNode.r + rightNode.l), leftNode.sum + rightNode.sum };
}
 
void func() {
    int l, r;
    cin >> M;
    while (M--) {
        cin >> l >> r;
        cout << query(11, N, l, r).max << '\n';
    }
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
    init(11, N);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > SegmentTree' 카테고리의 다른 글

boj 12895 화려한 마을  (0) 2022.09.07
boj 10167 금광  (0) 2022.09.02
boj 12846 무서운 아르바이트  (0) 2022.08.19
boj 1849 순열  (0) 2021.09.08
boj 14719 빗물  (0) 2021.03.15

알고리즘

1일 1알고리즘

 

다이아 승급!

갑자기 다이아 가고싶었고, 올리느라 힘들었다.

다이아 가자마자 스트릭 색부터 변경했다. ㅎㅎ

 

후기

벌써 8월이 끝나간다.

취업한지도 벌써 한달. 테스트만 진행 중이고, 블록체인 공부도 꾸준히 하는중이다.

요즘에 일찍 자보고는 있는데 여전히 피곤하더라 ㅎㅎㅎ

이정도면 그냥 피곤한게 아닐까.. 그래도 일찍 자는게 좋긴 한것같다.

블록체인, Solidity 등 어려운게 많은데 언제 적응할지는 모르겠다.

'잡담' 카테고리의 다른 글

9월 2주차 결산  (0) 2022.09.11
9월 1주차 결산  (0) 2022.09.05
8월 3주차 결산  (0) 2022.08.22
8월 2주차 결산  (0) 2022.08.14
8월 1주차 결산  (0) 2022.08.08

3년전에 대학 과제로 좌표 압축 기법을 활용하는 문제가 나왔었지만 이해를 못해서 풀지 못했습니다.

지금와서 보니 생각보다 간단했고, 정리하면서 포스팅합니다.

 

우선 좌표압축이란, 수의 범위가 크게 주어질 때 인덱스를 이용하여 범위를 줄이는 기법입니다.

코테에서 요구하는 기법은 아닐지라도 대회 준비를 하셨던 분이라면 보신 적이 있을 거라고 생각합니다.

그만큼 PS에서 많이 사용되는 기법이며, 한 번쯤 알아가는 것도 괜찮은 것 같습니다.

 

1차원 배열

https://www.acmicpc.net/problem/18870

 

18870번: 좌표 압축

수직선 위에 N개의 좌표 X1, X2, ..., XN이 있다. 이 좌표에 좌표 압축을 적용하려고 한다. Xi를 좌표 압축한 결과 X'i의 값은 Xi > Xj를 만족하는 서로 다른 좌표의 개수와 같아야 한다. X1, X2, ..., XN에 좌

www.acmicpc.net

이 문제가 가장 기본적으로 좌표 압축을 체험해볼 수 있는 문제입니다.

1차원 좌표를 압축하는 것이 문제에서 요구하는 것인데, 좌표의 범위가 -10억 ~ 10억입니다.

이 범위를 한번씩 돌며 순위를 부여하는데 너무 많은 시간이 필요합니다.

이를 해결하기 위한 기법이 좌표 압축입니다.

좌표의 범위는 -10억 ~ 10억이지만 N의 범위는 1 ~ 100만인 것을 이용합니다.

 

로직의 진행 순서입니다.

  1. 우선 입력을 받을 때, 원래 배열 순서를 유지해야 하므로 인덱스도 함께 저장합니다.
  2. 배열을 좌표를 기준으로 오름차순 정렬합니다.
  3. 이전 값을 사용하기 위한 pre와 압축된 값을 위한 cnt 변수를 선언, 초기화합니다.
  4. N만큼 for문을 돌면서 pre와 list[i] 값을 비교합니다.
  5. pre == list[i]이면 이전에 등장했던 수가 있으므로 cnt를 유지합니다.
  6. pre != list[i]이면 다른 수가 등장했으므로 cnt를 1 증가합니다.
  7. pre를 현재 좌표값으로 갱신하고, list[i]에 cnt를 넣어줍니다.
  8. 압축된 배열을 원래 배열 순서로 돌리기 위해 1번에서 저장했던 인덱스를 기준으로 오름차순 정렬합니다.

이렇게 되면

2 4 -10 4 -9

이었던 좌표가

2 3 0 3 1

이렇게 변환되는 것을 볼 수 있습니다.

 

 

이 방법 외에 unique를 이용하는 방법도 있습니다.

두 개의 벡터를 사용하며, 하나는 원래 벡터, 하나는 정렬하여 중복을 제거한 벡터로 사용됩니다.

중복을 제거한 후 lower_bound를 적용하면 원래 벡터에서 값의 순서를 알 수 있습니다.

 

원래 벡터를 w, 중복을 제거한 벡터를 v라고 했을 때,

로직의 진행 순서로는

  1. 입력 받은 값을 벡터 2개에 같이 넣어줍니다.
  2. 벡터 v를 오름차순으로 정렬합니다.
  3. erase와 unique를 이용하여 정렬된 벡터 v에서 중복을 제거합니다.
  4. N만큼 for문을 돌면서 lower_bound를 이용하여 w[i] 값의 압축된 값을 가져옵니다.

첫 번째 방법
두 번째 방법

간단해 보이지만 위의 문제에서는 첫 번째 방법이 메모리, 시간 부분에서 효율적인 것을 알 수 있습니다.

 

간단하게 소스도 첨부합니다.

 

첫 번째 방법

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
#include <iostream>
#include <algorithm>
#define MAX 1000000
using namespace std;
typedef pair<intint> pii;
 
pii list[MAX];
int N;
 
void func() {
    sort(list, list + N);
 
    int pre = 1e9 + 1;
    int cnt = -1;
    for (int i = 0; i < N; i++) {
        if (pre != list[i].first) cnt++;
        pre = list[i].first;
        list[i].first = cnt;
    }
    sort(list, list + N, [](pii a, pii b) {
        return a.second < b.second;
    });
 
    for (int i = 0; i < N; i++) {
        cout << list[i].first << ' ';
    }
    cout << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> list[i].first;
        list[i].second = i;
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

 

두 번째 방법

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
#include <iostream>
#include <algorithm>
#include <vector>
#define MAX 1000000
using namespace std;
 
vector<int> v, w;
int N;
 
void func() {
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
 
    for (int i = 0; i < N; i++) {
        cout << lower_bound(v.begin(), v.end(), w[i]) - v.begin() << ' ';
    }
    cout << '\n';
}
 
void input() {
    int x;
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> x;
        v.push_back(x);
        w.push_back(x);
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

 

2차원 배열

https://www.acmicpc.net/problem/10167

 

10167번: 금광

첫 줄에는 금광들의 개수 N (1 ≤ N ≤ 3,000)이 주어진다. 이어지는 N개의 줄 각각에는 금광의 좌표 (x, y)를 나타내는 음이 아닌 두 정수 x와 y(0 ≤ x, y ≤ 109), 그리고 금광을 개발하면 얻게 되는 이

www.acmicpc.net

이 문제는 좌표 압축만을 요구하지는 않지만 2차원 배열 좌표압축을 해야하는 문제입니다.

세그먼트 트리처럼 구간 쿼리를 사용하는 문제에 좌표 압축을 적용할 수 있어 이 문제를 가져왔습니다.

전체 풀이는 추후 포스팅할 예정이고, 좌표 압축에 해당하는 부분만 진행하려고 합니다.

 

사실 2차원 배열 압축은 위 방법들을 그대로 사용하면 됩니다.

1차원 배열에 적용했던 것들을 두 번 적용하는 것입니다.

 

첫 번째 방법
두 번째 방법

이 문제도 첫 번째 방법이 좀더 효율적으로 나왔습니다.

두 번째 방법이 더 간단할 수 있지만 저는 첫 번째 방법을 쓸 것 같습니다.

 

이 문제는 좌표 압축 코드만 첨부합니다.

 

첫 번째 방법

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
void func() {
    sort(list + 1, list + 1 + N, [](Point a, Point b) {
        return a.x < b.x;
    });
 
    int pre = 1e9 + 1;
    int xCnt = 0;
    for (int i = 1; i <= N; i++) {
        if (pre != list[i].x) xCnt++;
        pre = list[i].x;
        list[i].x = xCnt;
    }
 
    sort(list + 1, list + 1 + N, [](Point a, Point b) {
        return a.y < b.y;
    });
 
    pre = 1e9 + 1;
    int yCnt = 0;
    for (int i = 1; i <= N; i++) {
        if (pre != list[i].y) yCnt++;
        pre = list[i].y;
        list[i].y = yCnt;
    }
}
cs

 

두 번째 방법

1
2
3
4
5
6
7
8
9
10
11
12
void func() {
    sort(xtmp.begin(), xtmp.end());
    xtmp.erase(unique(xtmp.begin(), xtmp.end()), xtmp.end());
 
    sort(ytmp.begin(), ytmp.end());
    ytmp.erase(unique(ytmp.begin(), ytmp.end()), ytmp.end());
 
    for (int i = 1; i <= N; i++) {
        list[i].x = lower_bound(xtmp.begin(), xtmp.end(), list[i].x) - xtmp.begin() + 1;
        list[i].y = lower_bound(ytmp.begin(), ytmp.end(), list[i].y) - ytmp.begin() + 1;
    }
}
cs

 

알고리즘

1일 1알고리즘

 

후기

평소에 2 ~ 3시쯤 자는데 좀 피곤하다고 느껴서 생활 패턴을 좀 바꿔봐야겠다는 생각이 든다.

일찍 자고, 밖에도 좀 돌아다니고, 집에서도 공부하는 능력을 키워야겠다.

집에서 공부하는 사람 진짜 존경한다.

이번주에 추석 KTX 예매가 있다는 소식을 듣고, 바로 예매를 했다.

서버가 터지는바람에 1700번대에서 30000번대로 가버리긴 했지만 다른 사람들도 같은 경험을 한 덕분인지 원하는 시간대에 예약에 성공해서 다행이다.

'잡담' 카테고리의 다른 글

9월 1주차 결산  (0) 2022.09.05
8월 4주차 결산  (0) 2022.08.29
8월 2주차 결산  (0) 2022.08.14
8월 1주차 결산  (0) 2022.08.08
7월 4주차 결산  (0) 2022.08.01

https://www.acmicpc.net/problem/12846

 

12846번: 무서운 아르바이트

성화는 악독하기로 유명한 편의점 사장이다. 그의 편의점에는 특이한 임금 체계를 가지고 있다. 각 날마다 일의 차이때문에 일마다 급여가 정해져 있다. 돈은 당일에 주지 않고 퇴직을 할 때 한

www.acmicpc.net

일을 연속적으로 해야하며, 한 번만 할 수 있습니다.

또한 일을 한 기간동안 가장 작은 일급 * 기간의 돈을 받습니다.

 

가장 작은 일급이 10이고, 5일동안 일해서 받은 50보다

가장 작은 일급이 20이고, 3일동안 일해서 받은 60이 더 많은 이익을 챙길 수 있습니다.

 

이 문제는 히스토그램 문제와 요구하는 것이 같으며,

https://emoney96.tistory.com/139

 

boj 6549 히스토그램에서 가장 큰 직사각형

www.acmicpc.net/problem/6549 6549번: 히스토그램에서 가장 큰 직사각형 입력은 테스트 케이스 여러 개로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있고, 직사각형의 수 n이 가장 처음으로 주

emoney96.tistory.com

https://emoney96.tistory.com/138

 

boj 2104 부분배열 고르기

www.acmicpc.net/problem/2104 2104번: 부분배열 고르기 크기가 N(1≤N≤100,000)인 1차원 배열 A[1], …, A[N]이 있다. 어떤 i, j(1≤i≤j≤N)에 대한 점수는, (A[i]+…+A[j])×Min{A[i], …, A[j]}가 된다. 즉,..

emoney96.tistory.com

두 문제와 동일한 풀이입니다.

 

트리에는 가장 작은 일급을 가진 인덱스를 저장합니다.

init 함수로 전 구간의 최소 인덱스를 저장합니다.

그리고 partition 함수를 통해 해당 구간에서 가장 작은 값을 가지는 인덱스를 query 함수로 찾은 후 최대 이익을 갱신합니다.

해당 인덱스는 가장 작은 값이므로 제외하고, [s ~ idx - 1] [idx + 1 ~ e] 구간으로 나누어서 같은 과정을 반복합니다.

 

 

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
#include <iostream>
#include <algorithm>
#define MAX 100001
using namespace std;
typedef long long ll;
 
ll tree[MAX * 4], list[MAX], ans;
int N;
 
void init(int node, int s, int e) {
    if (s == e) {
        tree[node] = s;
        return;
    }
 
    int m = (s + e) / 2;
    init(node * 2, s, m);
    init(node * 2 + 1, m + 1, e);
 
    int l = tree[node * 2];
    int r = tree[node * 2 + 1];
    if (list[l] < list[r]) tree[node] = l;
    else tree[node] = r;
 
    return;
}
 
int query(int node, int s, int e, int l, int r) {
    if (l > e || s > r) return -1;
    if (l <= s && e <= r) return tree[node];
 
    int m = (s + e) / 2;
    int lIdx = query(node * 2, s, m, l, r);
    int rIdx = query(node * 2 + 1, m + 1, e, l, r);
 
    if (lIdx == -1) {
        return rIdx;
    }
    else if (rIdx == -1) {
        return lIdx;
    }
    else {
        return list[lIdx] < list[rIdx] ? lIdx : rIdx;
    }
}
 
void partition(int s, int e) {
    if (s > e) return;
 
    int idx = query(11, N, s, e);
    ans = max(ans, list[idx] * (ll)(e - s + 1));
    partition(s, idx - 1);
    partition(idx + 1, e);
}
 
void func() {
    init(11, N);
    partition(1, N);
 
    cout << ans << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > SegmentTree' 카테고리의 다른 글

boj 10167 금광  (0) 2022.09.02
boj 16993 연속합과 쿼리  (0) 2022.08.29
boj 1849 순열  (0) 2021.09.08
boj 14719 빗물  (0) 2021.03.15
boj 2304 창고 다각형  (0) 2021.02.25

알고리즘

1일 1알고리즘

그리고 1500문제 달성

 

후기

당분간은 테스팅 위주의 일을 할 것 같아서 스터디때 하려고 했던 스프링 공부를 이어서 하려고 한다.

그 전에 이전에 했던 것들 모든 파트들을 한번씩 복습해야 할 것 같다.

뿐만 아니라, vue나 서버 등 봐야할 것들도 많은것 같다.

저녁이나 주말에 친구들이랑 랜각공을 하려고 하는데 각자 분야는 다르지만 도움이 되지않을까 싶다.

'잡담' 카테고리의 다른 글

8월 4주차 결산  (0) 2022.08.29
8월 3주차 결산  (0) 2022.08.22
8월 1주차 결산  (0) 2022.08.08
7월 4주차 결산  (0) 2022.08.01
7월 3주차 결산  (0) 2022.07.24

https://www.acmicpc.net/problem/2015

 

2015번: 수들의 합 4

첫째 줄에 정수 N과 K가 주어진다. (1 ≤ N ≤ 200,000, |K| ≤ 2,000,000,000) N과 K 사이에는 빈칸이 하나 있다. 둘째 줄에는 배열 A를 이루는 N개의 정수가 빈 칸을 사이에 두고 A[1], A[2], ..., A[N]의 순서로

www.acmicpc.net

N이 20만이므로 모든 부분합을 직접 구할 수는 없습니다.

기존의 누적합 문제에서 부분합을 구하는 식은 dp[r] - dp[l - 1] = K 입니다.

K는 입력으로 주어지기 때문에 식을 변형하면 dp[r] - K = dp[l - 1]이 되고, 이 식으로 문제에 접근합니다.

 

식을 풀어서 설명하면 dp[r](1 ~ r번 요소의 누적합) - K = dp[l - 1](1 ~ l - 1번 요소의 누적합)

이렇게 되는데, l - 1번을 구할 필요는 없습니다. dp[r] - K와 일치하는 값이 몇 번 나왔는지 세어주면 되는겁니다.

이를 계산하기 위해 map을 사용합니다. 정렬이 필요없으므로 unordred_map을 사용하였습니다.

 

누적합을 구하면서 dp[i] - K 값이 map에 있다면 map[dp[i] - K]만큼 카운트를 증가시킵니다.

그리고 dp[i] 값을 새롭게 map에 넣어주거나 1을 증가시킵니다.

dp[i] 값이 K인 경우는 ans를 따로 증가시켜야 합니다.

 

N = 200000, K = 0이고, 배열의 값이 모두 0이라면 int 범위를 넘으므로 long long을 사용해야 합니다.

 

 

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
#include <iostream>
#include <unordered_map>
#define MAX 200001
using namespace std;
typedef long long ll;
 
int dp[MAX];
int N, K;
 
void func() {
    unordered_map<int, ll> m;
    ll ans = 0;
    for (int i = 1; i <= N; i++) {
        dp[i] += dp[i - 1];
        if (dp[i] == K) ans++;
 
        if (m.find(dp[i] - K) != m.end()) {
            ans += m[dp[i] - K];
        }
 
        if (m.find(dp[i]) != m.end()) {
            m[dp[i]]++;
        }
        else {
           m.insert({ dp[i], 1LL });
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    cin >> N >> K;
    for (int i = 1; i <= N; i++) {
        cin >> dp[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

 

'algorithm > dp' 카테고리의 다른 글

boj 14453 Hoof, Paper, Scissors (Silver)  (0) 2022.12.30
boj 2281 데스노트  (0) 2022.10.08
boj 21923 곡예 비행  (0) 2022.06.10
boj 12978 스크루지 민호 2  (0) 2022.06.08
boj 10986 나머지 합  (0) 2022.03.08

+ Recent posts