백준 5012번 불만 정렬
문제링크: https://www.acmicpc.net/problem/5012
사용한 알고리즘: 펜윅트리
3쌍으로 되는 inversion의 개수를 구하는 문제였습니다.
펜윅트리를 이용하여 i번째 수를 볼 때, 앞쪽에 있는 큰 수의 개수, 뒷쪽에 있는 작은 수 의 개수를 확인하며 풀어주었습니다.
풀이는 다음과 같습니다.
(1) 벡터 배열을 하나 만들어서 입력을 받을 때 마다 그 수의 위치를 저장해주었습니다.
(2) 큰 수부터 차례로 보면서 펜윅트리를 구현해주었습니다. ( 큰 수가 i 번째 위치에 있다고 하면 i번째 부터 끝까지 +1 을 하는 펜윅 트리를 만들어서 이후 그보다 작은 수를 볼 때 (j번째라고 합시다.) 1~j-1번째까지 쌓인 +1들의 합은 j 앞에 있는 이보다 큰수들의 개수의 합이고, j+1~N번째 있는 +1들의 합을 N-j에서 빼주면 그것이 j 뒤에 있는 이보다 작은수들의 개수의 합입니다.
(3) i번째에 위치한 숫자를 볼 때 생길 수 있는 3쌍의 inversion의 수는, 과정 (2)에 의하여 앞에 있는 큰 수들의 개수의 합: sum(i-1) 과 뒤에 있는 작은 수들의 개수의 합: (N-j)-(sum(N)-sum(j) 을 곱해주면 구할 수 있습니다.
펜윅트리를 이용하면 비교적 간단하게 풀 수 있는 문제였습니다.
혹시 펜윅트리에대한 자세한 내용이 궁금한 분들은 아래 링크에 잘 정리되어 잇으니 참고해주세요.
https://www.acmicpc.net/blog/view/21
사용한 알고리즘: 펜윅트리
3쌍으로 되는 inversion의 개수를 구하는 문제였습니다.
펜윅트리를 이용하여 i번째 수를 볼 때, 앞쪽에 있는 큰 수의 개수, 뒷쪽에 있는 작은 수 의 개수를 확인하며 풀어주었습니다.
풀이는 다음과 같습니다.
(1) 벡터 배열을 하나 만들어서 입력을 받을 때 마다 그 수의 위치를 저장해주었습니다.
(2) 큰 수부터 차례로 보면서 펜윅트리를 구현해주었습니다. ( 큰 수가 i 번째 위치에 있다고 하면 i번째 부터 끝까지 +1 을 하는 펜윅 트리를 만들어서 이후 그보다 작은 수를 볼 때 (j번째라고 합시다.) 1~j-1번째까지 쌓인 +1들의 합은 j 앞에 있는 이보다 큰수들의 개수의 합이고, j+1~N번째 있는 +1들의 합을 N-j에서 빼주면 그것이 j 뒤에 있는 이보다 작은수들의 개수의 합입니다.
(3) i번째에 위치한 숫자를 볼 때 생길 수 있는 3쌍의 inversion의 수는, 과정 (2)에 의하여 앞에 있는 큰 수들의 개수의 합: sum(i-1) 과 뒤에 있는 작은 수들의 개수의 합: (N-j)-(sum(N)-sum(j) 을 곱해주면 구할 수 있습니다.
펜윅트리를 이용하면 비교적 간단하게 풀 수 있는 문제였습니다.
혹시 펜윅트리에대한 자세한 내용이 궁금한 분들은 아래 링크에 잘 정리되어 잇으니 참고해주세요.
https://www.acmicpc.net/blog/view/21
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <bits/stdc++.h> | |
using namespace std; | |
const int MAX = 111111; | |
#define ll long long | |
ll N; | |
vector<ll> arr[MAX]; | |
ll ans, tree[MAX]; | |
int Li(int x){ | |
return x&(-x); | |
} | |
ll sum(int i){ | |
ll temp = 0; | |
while(i>0){ | |
temp += tree[i]; | |
i -= Li(i); | |
} | |
return temp; | |
} | |
void update(int i){ | |
while(i<=100001){ | |
tree[i] += 1LL; | |
i += Li(i); | |
} | |
} | |
bool cmp(const int &a, const int &b){ | |
return a > b; | |
} | |
int main(){ios_base::sync_with_stdio(false); cout.tie(NULL); cin.tie(NULL); | |
cin >> N; | |
for (ll i = 1; i <= N; ++i){ | |
ll num; | |
cin >> num; | |
arr[num].push_back(i); | |
} | |
// 큰 수부터 볼꺼에요 | |
for (ll i = 100000; i > 0; i--){ | |
if(arr[i].empty()) continue; | |
// 큰 수의 각 위치들 중 맨 뒤 위치부터 보자 | |
sort(arr[i].begin(),arr[i].end(),cmp); | |
for(auto where: arr[i]){ | |
update(where); | |
//앞에 있는 수 중 자신보다 큰 수의 개수 | |
ll leftcnt = sum(where-1); | |
//뒤에 있는 수 중 자신보다 작은 수의 개수 | |
ll rightcnt = (N-where)-(sum(N)-sum(where)); | |
ans+=leftcnt*rightcnt; | |
} | |
} | |
cout << ans << '\n'; | |
return 0; |
댓글
댓글 쓰기
긴 글 읽어주셔서 감사합니다.
궁금한게 있으시다면 댓글 달아주세요!