본문 바로가기

Dot Algo∙ DS/PS

[BOJ] 백준 10090번 Counting Inversions (Java)

#10090 Counting Inversions

난이도 : 플레 5

유형 : 세그먼트 트리 

 

10090번: Counting Inversions

A permutation of integers from 1 to n is a sequence a1, a2, ..., an, such that each integer from 1 to n is appeared in the sequence exactly once. Two integers in а permutation form an inversion, when the bigger one is before the smaller one. As an example

www.acmicpc.net

▸ 문제

A permutation of integers from 1 to n is a sequence a1, a2, ..., an, such that each integer from 1 to n is appeared in the sequence exactly once.

Two integers in а permutation form an inversion, when the bigger one is before the smaller one.

As an example, in the permutation 4 2 7 1 5 6 3, there are 10 inversions in total. They are the following pairs: 4–2, 4–1, 4–3, 2–1, 7–1, 7–5, 7–6, 7–3, 5–3, 6–3.

Write program invcnt that computes the number of the inversions in a given permutation.

 입력

The value for the number n is written on the first line of the standard input. The permutation is written on the second line: n numbers, delimited by spaces.

  • 2 ≤ n ≤ 1000000
     

 출력

Write the count of inversions on the standard output.

 

문제 풀이  

자신보다 앞에 있는 수들 중 자신보다 작은 값의 개수를 카운팅해주는 문제이다. n의 최대는 100만이기 때문에 선형탐색으로 하면 시간초과가 발생한다. 그래서 분할정복아니면 세그먼트 트리 자료구조를 이용하여 O(nlogn)의 시간복잡도까지 줄여줘야 한다.

 

4 2 7 1 5 6 3을 차례대로 인덱스 트리에 넣어 자신보다 앞에 자신보다 작은 값의 개수를 구하면 된다.

  • 4는 1, ,2, 3이 아직 트리에 입력되지 않았으므로 카운트 3, 2는 1이 아직 트리에 입력되지 않았으므로 카운트 1 ... 이런식으로 해주면 된다.
  • 그럼 쿼리는 값 x라 할 때, (x-1)의 수들 중 현재 인덱스 트리에 들어있는 수(query(1~x-1))를 빼주면 된다.
  • int res = (x-1) + query(1~x-1)이다.

 

그럼 입력을 반대로 넣어주면 간단하게 query(1~x-1)로만도 Inversion Counting 값을 구할 수 있다. 이미 들어있는 수들 중에 자신보다 작은 값을 찾아주면 되기 때문이다.

3 입력 

  • 3 입력 → query(1, 2) 자신보다 앞에 3보다 작은 수가 입력된 것이 없으므로 0

 

3 입력 

  • 6 입력 → query(1, 5) 자신보다 앞에 6보다 작은 수가 3 있으므로 1 카운팅

 

이렇게 구한 수를 모두 더해 출력해주면 된다.

 

풀이 코드 

import java.io.*;
import java.util.StringTokenizer;

public class Main {

	static int[] tree;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		int[] arr = new int[n+1];
		for(int i=1; i<=n; i++) {
			arr[i] = Integer.parseInt(st.nextToken());
		}
		
		tree = new int[n*4];
		long res = 0;
		for(int i=n; i>0; i--) {
			int x = arr[i];
			res += query(1, n, 1, 1, x-1);
			update(1, n, 1, x);	
		}
        System.out.println(res);
	}
    
	static int update(int s, int e, int node, int idx) {
		if(e < idx || idx < s) return tree[node];
		if(s == e) return tree[node] +=1 ;
		
		int mid = (s+e)/2;
		return tree[node] = update(s, mid, node*2, idx) + update(mid+1, e, node*2+1, idx);
	}
	
	static int query(int s, int e, int node, int l, int r) {
		if(r < s || e < l) return 0;
		if(l <= s && e <= r)return tree[node];
		int mid = (s+e)/2;
		return query(s, mid, node*2, l, r) + query(mid+1, e, node*2+1, l, r);
	}
}