본문 바로가기

Problem Solving/Baekjoon OJ

[BOJ 2867] 수열의 값

 

2867번: 수열의 값

첫째 줄에 수열의 크기 N(2<=N<=300,000)이 주어진다. 둘째 줄부터 N개의 줄에는 수열의 원소가 한 줄에 하나씩 주어진다. 원소는 100,000,000보다 크지 않은 양의 정수이다.

www.acmicpc.net

 

결국 백준에 손을 댔다(....) 그룹의 채점 현황을 구경하다가 이 문제를 보았는데, 풀이가 쉽게 생각나서 풀 수밖에 없었다..

 

나름 교육적인 문제이니 한 번 풀어보자.

 

더보기

$N$이 크기 때문에 $O(NlogN)$ 이상의 풀이를 생각해내야 한다. 이런 류의 문제는 주로 슬라이딩 윈도우 또는 세그먼트 트리를 이용하는데, 태그를 보면(?) 스택이라 하니 $O(N)$에 풀릴 거라고 추측할 수 있다. 이제 $i$번째 원소를 끝으로 하는 부분 수열의 값의 합을 $O(1)$에 구하는 것을 목적으로 해보자.

 

$ sum_{i}=\sum_{1\leq j\leq i}^{}val[a_{j},a_{j+1},\cdots,a_{i}] $ 을 $ O(1) $에 구하자. 누적합을 이용해야 될 것처럼 보이는데, 각 수열에서의 최대, 최소를 함께 생각하기가 힘들다. 하지만 $ sum_{i}=\sum_{1\leq j\leq i}^{}max[a_{j},a_{j+1},\cdots,a_{i}]-\sum_{1\leq j\leq i}^{}min[a_{j},a_{j+1},\cdots,a_{i}] $ 이기 때문에 최대, 최소를 따로 관리해줄 수 있다. 최솟값의 경향성을 생각해보면 단조 감소 수열을 이룬다는 것을 쉽게 알 수 있고, 최댓값도 마찬가지로 단조 증가수열을 이룰 것이다. 그렇다면 기존의 $[a_{1},\cdots,a_{i-1}]$에서 $a_{i}$가 추가될 때 뒤에서부터 연속한 부분이 변화함을 관찰할 수 있다. 이를 스택과 DP를 이용하면 스택과 누적합의 갱신을 $ O(N/N+1) $에 해결할 수 있다.

 

더보기
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node { ll val,idx; };
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int n; cin >> n;
	vector<ll> sn(n+1),sx(n+1);
	ll x,ans=0;
	stack<node> stn,stx;
	stn.push({0,0}); stx.push({(ll)1e9+1,0});
	for(int i=1;i<=n;i++){
		cin >> x;
		while(stn.top().val>x) stn.pop();
		while(stx.top().val<x) stx.pop();
		sn[i]=sn[stn.top().idx]+(i-stn.top().idx)*x;
		sx[i]=sx[stx.top().idx]+(i-stx.top().idx)*x;
		ans+=sx[i]-sn[i];
		stn.push({x,i}); stx.push({x,i});
	}
	cout << ans;
}

 

이번 글을 쓰면서 수식입력기의 필요성을 느꼈다. 시험 끝나고 배워보자.

+ 21.07.17 추가 : 수식입력기를 이용해서 풀이를 전면 수정했다.

'Problem Solving > Baekjoon OJ' 카테고리의 다른 글

[BOJ 20188] 등산 마니아  (0) 2021.07.18
Diamond I 달성  (5) 2021.07.03
[BOJ 2867] 수열의 값  (2) 2021.06.12
Diamond II 달성  (4) 2021.05.30
Class 8 취득  (0) 2021.05.27
Diamond III 달성  (0) 2021.04.10