자녀 줄세우기 문제 풀이

자녀 줄세우기

출처 제5회 랴오차오컵 지역 C++ B그룹
알고리즘 트리 배열 그리디 병합 정렬

문제 설명

n명의 아이들이 일렬로 서 있습니다.

이제 키가 작은 순서대로 정렬해야 하지만, 인접한 두 아이만 교체할 수 있습니다.

각 아이에는 불만도가 있습니다.

처음에는 모든 아이들의 불만도가 0입니다.

어떤 아이가 처음 교체를 요청받으면 불만도가 1 증가하고, 두 번째 교체를 요청받으면 불만도가 2 증가합니다(즉, 불만도는 3이 됩니다). 이런 식으로 k번째 교체를 요청받을 때마다 불만도가 k만큼 증가합니다.

모든 아이들을 키 순으로 정렬하기 위해 필요한 불만도의 합의 최솟값은 얼마입니까?

키가 같은 아이들이 있다면, 서로의 순서는 상관없습니다.

입력 형식

첫 번째 줄에는 아이들의 수를 나타내는 정수 n이 포함됩니다.

두 번째 줄에는 각 아이의 키를 나타내는 n개의 정수 H1, H2, ..., Hn이 포함됩니다.

출력 형식

한 줄에 아이들의 불만도 합의 최솟값을 나타내는 정수를 출력합니다.

데이터 범위
1≤n≤100000,
0≤Hi≤1000000
입력 예시:
3
3 2 1
출력 예시:
9
예시 설명

먼저 키가 3과 2인 아이들을 교체한 다음, 키가 3과 1인 아이들을 교체하고, 마지막으로 키가 2와 1인 아이들을 교체합니다. 각 아이의 불만도는 3이므로 총합은 9입니다.

해결 방법

직접적으로 n^2 접근은 데이터가 너무 커서 비효율적입니다. 대신, 각 숫자의 교체 횟수는 이 숫자보다 큰 숫자의 개수와 작은 숫자의 개수의 합과 같다는 것을 이해할 수 있습니다. 또한, '어떤 아이가 k번째 교체를 요청받을 때마다 불만도가 k만큼 증가한다'는 점을 고려하면 n*(n-1)/2 공식을 사용하여 계산할 수 있습니다. 그런 다음 모든 아이들의 불만도 값을 누적하면 됩니다.

C++ 코드
#include<iostream>
#include<cstring>

using namespace std;
typedef long long LL;
const int MAX = 1e6 + 10;
int heights[MAX], counts[MAX], fenwick[MAX]; // 키, 개수, 펜윅 트리
int n;

int lowbit(int x) { return x & -x; }
void update(int x) {
    for(int i = x; i < MAX; i += lowbit(i)) 
        fenwick[i]++;
}
int query(int x) {
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) 
        res += fenwick[i];
    return res;
}
int main() {
    cin >> n;
    for(int i = 0; i < n; i++) {
        cin >> heights[i];
        heights[i]++; // 펜윅 트리는 1부터 시작해야 함
        
        counts[i] = query(MAX - 1) - query(heights[i]); // i보다 큰 수
        update(heights[i]);
    }
    
    memset(fenwick, 0, sizeof fenwick); // 펜윅 트리 초기화
    
    for(int i = n - 1; i >= 0; i--) {
        counts[i] += query(heights[i] - 1); // i보다 작은 수
        update(heights[i]);
    }
    
    LL result = 0;
    for(int i = 0; i < n; i++) 
        result += (LL)counts[i] * (counts[i] + 1) / 2; // 공식 계산 및 누적
        
    cout << result;
    
    return 0;
}

태그: 트리 배열 그리디 알고리즘 병합 정렬

5월 20일 00:27에 게시됨