티스토리 뷰
최댓값과 최솟값은 별개다. 즉, 모든 부분수열에서 최댓값의 합 - 최솟값의 합을 구하면 되는 문제다.
주어진 수열을 \(a\)라고 하자. 그리고 현재 보고 있는게 \(i\)번쨰 항이라는건 \(i\)번째 항으로 끝나는 부분수열만 보겠다는 뜻이라고 하자.
우선 최댓값과 최솟값은 같은 방법으로 구할 수 있을테니 최댓값만 생각해보자.
\(a_i\)가 \(1\sim i\)번째 항까지의 최댓값이라고 해보자. 그렇다면 \(a_i\)로 끝나는 부분수열은 항상 \(a_i\)가 최댓값일 수밖에 없다.
\(a_{i-2}\)가 \(1\sim i\)번째 항까지의 최댓값이고, \(a_i\)가 그 이후의 최댓값이라고 해보자. 그렇다면 \(a_i\)로 끝나는 부분수열 중 \(i-2\)개는 \(a_{i-2}\)가 최댓값이고 2개는 \(a_i\)가 최댓값이다.
그래서 최댓값을 내림차순 스택으로 관리할 수 있다. 스택이 내림차순으로 되도록 원소를 넣으면서, \(a_i\)가 최댓값인 경우가 몇 번 있는지를 따로 계산해주면 된다. 뺄 때는 더해졌던만큼 다시 계산하면 된다.
마찬가지로 최솟값은 오름차순 스택으로 관리해서 \(a_i\)로 끝나는 부분수열의 최댓값 - 최솟값을 \(O(n/n)\)만에 구할 수 있다.
더보기
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
|
#include <bits/stdc++.h>
using namespace std;
long long arr[300000], stk1tot, stk2tot;
int stk1[300001], stk2[300001], e1, e2;
int main() {
int n;
scanf("%d", &n);
for (int i = 0;i < n;++i)
scanf("%lld", &arr[i]);
long long ans = 0;
stk1[e1++] = -1;
stk1[e1++] = 0;
stk1tot += arr[0];
stk2[e2++] = -1;
stk2[e2++] = 0;
stk2tot += arr[0];
for (int i = 1;i < n;++i) {
stk1tot += mntot;
while (e1 - 2 >= 0 && arr[stk1[e1 - 1]] > arr[i]) {
stk1tot -= (stk1[e1 - 1] - stk1[e1 - 2]) * arr[stk1[e1 - 1]];
e1--;
}
stk1tot += (i - stk1[e1 - 1]) * arr[i];
stk1[e1++] = i;
stk2tot += mxtot;
while (e2 - 2 >= 0 && arr[stk2[e2 - 1]] < arr[i]) {
stk2tot -= (stk2[e2 - 1] - stk2[e2 - 2]) * arr[stk2[e2 - 1]];
e2--;
}
stk2tot += (i - stk2[e2 - 1]) * arr[i];
stk2[e2++] = i;
ans -= stk1tot;
ans += stk2tot;
}
printf("%lld", ans);
return 0;
}
|
cs |
'문제' 카테고리의 다른 글
BOJ 16456 하와와 대학생쨩 하와이로 가는 거시와요~ (0) | 2018.11.20 |
---|---|
Codeforces 1077F1, 1077F2 Pictures with Kittens (easy, hard version) (0) | 2018.11.20 |
Codeforces 1070C Cloud Computing (0) | 2018.11.11 |
BOJ 1819 불끄기 (0) | 2018.10.25 |
BOJ 12945 재미있는 박스 정리 (0) | 2018.09.26 |
댓글