https://www.acmicpc.net/problem/3770

 

세그먼트 트리문제다. [7578] 공장 문제와 같은 풀이로 접근가능하다.

 

풀이

[7578] 공장 문제와 달리 유의할 점은 입력되는 간선의 정렬이 필요하다는 점이다.

 

출발지 기준 오름차순, 출발지가 같다면 도착지기준 오름차순으로 고속도로 정보를 정렬한 뒤, 매 순간 도착도시 다음번호 ~ 마지막 도착도시 구간의 건설된 고속도로의 개수 총합을 출력하면 된다.

 

입력 예제를 기준으로 시나리오를 그려보면 다음과 같다.

 

좌측의 N개의 도시, 우측의 M개의 도시와 정렬된 간선 그리고 구간 합 세그먼트 트리를 생성한다.

 

1번 도시와 4번 도시를 연결, 4번은 가장 마지막 도시이므로 구간합 조회 시 범위를 넘어가게 되어 0을 반환하게 된다.

다음 탐색을 위해 4번 노드에 1 추가.

 

2번 도시와 3번 도시 연결, 이때 3번 도시 이후의 구간 즉 4~4번 구간의 고속도로의 개수 1을 정답에 추가한다.

이후 3번 노드에 1 추가.

 

3번 도시와 1번 도시 연결, 2번 도시부터 4번 도시 구간의 고속도로의 수를 조회한다. 고속도로의 개수 2가 반환된다.

이후 1번 노드에 1 추가.

 

마지막으로 3번 도시와 2번 도시 연결, 3번 도시와 4번 도시 구간합을 조회. 정답에 해당 구간값 2를 추가한다.

2번 노드에 1 추가.

 

정답 코드

import Foundation
//by rhyno
final class FileIO {
private let buffer:[UInt8]
private var index: Int = 0
init(fileHandle: FileHandle = FileHandle.standardInput) {
buffer = Array(try! fileHandle.readToEnd()!)+[UInt8(0)] // 인덱스 범위 넘어가는 것 방지
}
@inline(__always) private func read() -> UInt8 {
defer { index += 1 }
return buffer[index]
}
@inline(__always) func readInt() -> Int {
var sum = 0
var now = read()
var isPositive = true
while now == 10
|| now == 32 { now = read() } // 공백과 줄바꿈 무시
if now == 45 { isPositive.toggle(); now = read() } // 음수 처리
while now >= 48, now <= 57 {
sum = sum * 10 + Int(now-48)
now = read()
}
return sum * (isPositive ? 1:-1)
}
}
let io = FileIO()
let T = io.readInt()
var ans = [String]()
for tc in 1...T{
let N = io.readInt()
let M = io.readInt()
let K = io.readInt()
var result = 0
var tree = Array(repeating: 0, count: M*4)
var edges = [(u:Int,v:Int)]()
func update_tree(start:Int, end:Int, node:Int, idx:Int){
if idx < start || end < idx{ return }
tree[node] += 1
if start == end{ return }
let mid = (start+end)/2
update_tree(start: start, end: mid, node: node*2, idx: idx)
update_tree(start: mid+1, end: end, node: node*2+1, idx: idx)
}
func read_tree(start:Int, end:Int, node:Int, from:Int, to:Int) -> Int{
if end<from || to<start || to<from{ return 0 }
if from<=start && end<=to{ return tree[node] }
let mid = (start+end)/2
let left = read_tree(start: start, end: mid, node: node*2, from: from, to: to)
let right = read_tree(start: mid+1, end: end, node: node*2+1, from: from, to: to)
return left+right
}
for _ in 0..<K{
let u = io.readInt()-1
let v = io.readInt()-1
edges.append((u,v))
}
edges.sort(by: {
if $0.u == $1.u{
return $0.v < $1.v
}else{
return $0.u < $1.u
}
})
for edge in edges{
let v = edge.v
result += read_tree(start: 0, end: M-1, node: 1, from: v+1, to: M-1)
update_tree(start: 0, end: M-1, node: 1, idx: v)
}
ans.append("Test case \(tc): \(result)")
}
print(ans.joined(separator: "\n"))
view raw 3770.swift hosted with ❤ by GitHub

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

[13334] 철로  (0) 2023.03.26
[3895] 그리고 하나가 남았다  (0) 2023.03.24
[3653] 영화 수집  (0) 2023.03.12
[7578] 공장  (0) 2023.03.11
[1280] 나무 심기  (1) 2023.03.09

https://www.acmicpc.net/problem/3653

 

3653번: 영화 수집

각 테스트 케이스에 대해서 한 줄에 m개의 정수를 출력해야 한다. i번째 출력하는 수는 i번째로 영화를 볼 때 그 영화의 위에 있었던 DVD의 개수이다. 상근이는 매번 영화를 볼 때마다 본 영화 DVD

www.acmicpc.net

세그먼트 트리 문제다. 접근방법이 떠오르지 않아 다른 분의 풀이를 보고 나서 풀었다.

 

풀이

n개의 DVD 중 임의의 DVD를 m번 선택한 뒤 시청한 뒤 가장 위에 놓는다는 얘기는 세그먼트 트리의 입장에서 n개의 DVD앞에 m개의 빈 공간이 필요하다는 얘기가 된다. 리프노드 m+n개를 표현할 수 있는 구간 합 세그먼트트리를 생성한다. 끝에서 n개 구간(m ~ m+n)에 하나씩 추가하여 처음 상태의 DVD 위치를 지정한다.

 

X번 DVD가 선택되면 [0 ~ X-1] 구간의 구간합을 반환하고 해당 DVD를 m-0번 구간으로 이동, 다음 선택된 DVD는 m-1번 구간으로 이동하면서 반복하면 된다. 예제를 기준으로 다음과 같이 그래프 상태를 그려볼 수 있다.

 

초기상태의 트리와 배열의 모습이다. 배열에는 1번부터 3번 DVD가 트리의 어느 구간에 있는지 나타낸다. 

 

가장 먼저 3번 DVD의 경우 5번에 위치. 즉 0~4번까지의 구간합을 출력한다. 

이후 자신의 위치를 DVD 진열의 최상단인 2번 인덱스로 이동. 이후 3번 DVD의 위치를 갱신해 준다.

 

다음으로 1번 DVD의 위치는 3번이니 0~2번 구간합을 출력한다. 

이후 해당 DVD는 다음 최상단 인덱스인 1번으로 이동. 

 

1번 DVD를 확인. 이번에는 0~0 구간의 구간합을 출력하면 된다.

마지막 위치인 0번으로 위치 갱신.

 

정답 코드

import Foundation
let T = Int(readLine()!)!
var ans = [String]()
for _ in 0..<T{
let nm = readLine()!.split(separator: " ").map{Int($0)!}
let cmd = readLine()!.split(separator: " ").map{Int($0)!-1}
var result = [String]()
let n = nm[0]
let m = nm[1]
var arr = Array(repeating: 0, count: n)
var tree = Array(repeating: 0, count: (n+m)*4)
func update_tree(start:Int, end:Int, node:Int, idx:Int, value:Int){
if idx < start || end < idx{
return
}
tree[node] += value
if start == end{ return }
let mid = (start+end)/2
update_tree(start: start, end: mid, node: node*2, idx: idx, value: value)
update_tree(start: mid+1, end: end, node: node*2+1, idx: idx, value: value)
}
func read_tree(start:Int, end:Int, node:Int, from:Int, to:Int) -> Int{
if end < from || to < start{
return 0
}
if from <= start && end <= to{
return tree[node]
}
let mid = (start+end)/2
let left = read_tree(start: start, end: mid, node: node*2, from: from, to: to)
let right = read_tree(start: mid+1, end: end , node: node*2+1, from: from, to: to)
return left + right
}
for i in 0..<n{
update_tree(start: 0, end: n+m-1, node: 1, idx: i+m, value: 1)
arr[i] = i+m
}
var newNumber = m-1
for i in 0..<m{
let number = cmd[i]
let idx = arr[number]
result.append("\(read_tree(start: 0, end: n+m-1, node: 1, from: 0, to: idx-1))")
update_tree(start: 0, end: n+m-1, node: 1, idx: idx, value: -1)
update_tree(start: 0, end: n+m-1, node: 1, idx: newNumber, value: 1)
arr[number] = newNumber
newNumber -= 1
}
ans.append(result.joined(separator: " "))
}
print(ans.joined(separator: "\n"))
view raw 3653.swift hosted with ❤ by GitHub

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

[3895] 그리고 하나가 남았다  (0) 2023.03.24
[3770] 대한민국  (1) 2023.03.14
[7578] 공장  (0) 2023.03.11
[1280] 나무 심기  (1) 2023.03.09
[1725] 히스토그램  (0) 2023.03.07

https://www.acmicpc.net/problem/7578

 

7578번: 공장

어떤 공장에는 2N개의 기계가 2열에 걸쳐 N개씩 배치되어 있다. 이 2개의 열을 각각 A열과 B 열이라고 부른다. A열에 있는 N개의 기계는 각각이 B열에 있는 N개의 기계와 하나씩 짝을 이루어 케이블

www.acmicpc.net

세그먼트 트리 문제다.

 

풀이

A열의 좌측부터 연결해 가면서 계산이 이루어져야 한다. 각 A열의 기계번호가 B열에서 몇 번째에 위치한 지 찾아낸 뒤, 해당 위치부터 N-1번째 까지 몇 개의 기계가 연결되어 있는지 개수를 저장하면 된다. 구간 합 세그먼트트리와 딕셔너리를 이용하여 접근하였다.

 

예제를 기준으로 설명하면 다음과 같다.

A열의 0번 기계는 B열의 2번에 위치해 있다. 0~4까지의 연결된 기계의 개수를 조회한다.

구간에 연결된 기계가 하나도 없으므로 +0.

 

다음으로 A열 1번 기계는 B열의 0번에 위치한다. 0~4구간의 연결된 기계의 개수 조회.

한 개(2번)가 있으므로 +1.

 

A열 2번은 B열의 3번에 위치. 3~4구간 조회.

하나도 없으므로 +0.

 

A열 4번은 B열의 1번에 위치하여 1~4구간 조회.

두 개(2번, 3번)가 있으므로 +2.

 

마지막으로 A열 4번은 B열의 4번에 위치.

마지막 인덱스이므로 뒤에 연결된 기계가 없다. 즉 정답은 3을 출력한다.

 

정답 코드

import Foundation
//by rhyno
final class FileIO {
private let buffer:[UInt8]
private var index: Int = 0
init(fileHandle: FileHandle = FileHandle.standardInput) {
buffer = Array(try! fileHandle.readToEnd()!)+[UInt8(0)] // 인덱스 범위 넘어가는 것 방지
}
@inline(__always) private func read() -> UInt8 {
defer { index += 1 }
return buffer[index]
}
@inline(__always) func readInt() -> Int {
var sum = 0
var now = read()
var isPositive = true
while now == 10
|| now == 32 { now = read() } // 공백과 줄바꿈 무시
if now == 45 { isPositive.toggle(); now = read() } // 음수 처리
while now >= 48, now <= 57 {
sum = sum * 10 + Int(now-48)
now = read()
}
return sum * (isPositive ? 1:-1)
}
}
let io = FileIO()
let N = io.readInt()
var A = [Int]()
for _ in 0..<N{ A.append(io.readInt()) }
var B = [Int:Int]()
for i in 0..<N{ B[io.readInt()] = i }
var tree = Array(repeating: 0, count: N*4)
var ans = 0
func update_tree(start:Int, end:Int, node:Int, target_idx:Int){
if target_idx < start || end < target_idx{
return
}
tree[node] += 1
if start == end{
return
}
let mid = (start+end)/2
update_tree(start: start, end: mid, node: node*2, target_idx: target_idx)
update_tree(start: mid+1, end: end, node: node*2+1, target_idx: target_idx)
}
func read_tree(start:Int, end:Int, node:Int, from:Int, to:Int) -> Int{
if end < from || to < start{
return 0
}
if from <= start && end <= to{
return tree[node]
}
let mid = (start+end)/2
let left = read_tree(start: start, end: mid, node: node*2, from: from, to: to)
let right = read_tree(start: mid+1, end: end, node: node*2+1, from: from, to: to)
return left + right
}
for i in 0..<N{
let idx = B[A[i]]!
ans += read_tree(start: 0, end: N-1, node: 1, from: idx, to: N-1)
update_tree(start: 0, end: N-1, node: 1, target_idx: idx)
}
print(ans)
view raw 7578.swift hosted with ❤ by GitHub

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

[3770] 대한민국  (1) 2023.03.14
[3653] 영화 수집  (0) 2023.03.12
[1280] 나무 심기  (1) 2023.03.09
[1725] 히스토그램  (0) 2023.03.07
[1849] 순열  (0) 2023.03.05

https://www.acmicpc.net/problem/1280

 

1280번: 나무 심기

첫째 줄에 나무의 개수 N (2 ≤ N ≤ 200,000)이 주어진다. 둘째 줄부터 N개의 줄에 1번 나무의 좌표부터 차례대로 주어진다. 각각의 좌표는 200,000보다 작은 자연수 또는 0이다.

www.acmicpc.net

세그먼트 트리 문제다.

 

풀이

i번째 나무의 좌표 A에 대한 비용은 다음과 같이 정리가 가능하다. 

|(A -[i-1] 번 나무 좌표)| + |(A - [i-2] 번 나무좌표)| +... + |(A - [i-i] 번 나무 좌표)|
->  | A*i - ([i-1]번 나무 좌표 + [i-2] 번 나무좌표 +... + [i-i] 번 나무좌표) |

즉 해당 부분을 여태 심어진 나무의 갯수와 좌표의 누적값을 통해 계산이 가능하다는 것이다. 따라서 0부터 199,999 좌표의 나무개수와 해당 구간의 누적 좌표값을 표현하는 세그먼트 트리를 생성하여 다음과 같이 계산이 가능해진다.

좌측 비용 = (A보다 좌측에 있는 나무의 갯수 * A) - (A보다 좌측 좌표에 심어진 나무의 거리(좌표) 총합)
우측 비용 = ((A보다 우측 좌표에 심어진 나무의 거리(좌표) 총합) - (A보다 우측에 있는 나무의 개수 * A)
A좌표에 나무를 심는 비용 = 좌측비용 + 우측비용

매 입력마다 해당 좌표의 비용을 구하여 계산한 뒤, 다음 나무의 비용 계산을 위해 나무의 갯수를 갱신해 주면 값을 구할 수 있다.

 

정답 코드

import Foundation
let MOD = 1000000007
let N = Int(readLine()!)!
var tree = Array(repeating: (count:0,length:0), count: 200000*4)
var ans = 1
func update_count(start:Int, end:Int, node:Int, target_idx:Int){
if target_idx < start || end < target_idx{ return }
tree[node].count += 1
tree[node].length += target_idx
if start == end{ return }
let mid = (start+end)/2
update_count(start: start, end: mid, node: node*2, target_idx: target_idx)
update_count(start: mid+1, end: end, node: node*2+1, target_idx: target_idx)
}
func read_info(start:Int, end:Int, node:Int, min:Int, max:Int) -> (Int,Int){
if end < min || max < start{ return (0,0) }
if min <= start && end <= max{ return tree[node] }
let mid = (start+end)/2
let left = read_info(start: start, end: mid, node: node*2, min: min, max: max)
let right = read_info(start: mid+1, end: end, node: node*2+1, min: min, max: max)
return (left.0+right.0, left.1+right.1)
}
for idx in 0..<N{
var cost = 0
let coord = Int(readLine()!)!
let left = read_info(start: 0, end: 199999, node: 1, min: 0, max: coord)
let right = read_info(start: 0, end: 199999, node: 1, min: coord+1, max: 199999)
cost += coord*left.0 - left.1
cost += right.1 - coord*right.0
cost %= MOD
update_count(start: 0, end: 199999, node: 1, target_idx: coord)
if idx == 0 { continue }
ans *= cost
ans %= MOD
}
print(ans)
view raw 1280.swift hosted with ❤ by GitHub

세그먼트 트리의 활용은 정말 다양한 것 같다.

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

[3653] 영화 수집  (0) 2023.03.12
[7578] 공장  (0) 2023.03.11
[1725] 히스토그램  (0) 2023.03.07
[1849] 순열  (0) 2023.03.05
[1517] 버블 소트  (0) 2023.03.02

https://www.acmicpc.net/problem/1725

 

1725번: 히스토그램

첫 행에는 N (1 ≤ N ≤ 100,000) 이 주어진다. N은 히스토그램의 가로 칸의 수이다. 다음 N 행에 걸쳐 각 칸의 높이가 왼쪽에서부터 차례대로 주어진다. 각 칸의 높이는 1,000,000,000보다 작거나 같은

www.acmicpc.net

세그먼트트리로 접근한 문제다. 

 

풀이

구간 최솟값의 인덱스를 반환하는 세그먼트트리를 생성한다. 0~N-1 범위의 최소 인덱스를 찾은 뒤, 해당 인덱스를 사용하여 히스토그램의 넓이를 계산, 이후 해당 [0 ~ 최솟값인덱스-1] 범위와 [최솟값 인덱스+1 ~ N-1] 범위의 히스토그램 넓이를 계산해 나가면 된다. 

예제 [2,1,4,5,1,3,3]을 기준으로 다음과 같이 동작한다.

 

최솟값 인덱스 세그먼트트리 생성

 

가장 먼저 0~6 전체 범위의 히스토그램 영역을 탐색한다.

1번 인덱스가 반환되며 해당 블록의 높이인 1과 밑변에 해당하는 0~6 범위(6-0+1)를 곱해 넓이를 계산한다.

 

이후 해당 최소값 인덱스를 기준으로 좌, 우 영역의 넓이를 계산해 나가면 된다.

좌측의 경우 2*1 = 2가 반환되며 우측의 경우 1*5 = 5가 계산된다. 

이후 좌측의 경우 범위의 시작값과 마지막값이 동일하므로 종료.

우측의 경우 아직 탐색할 범위가 남았으므로 좌측과 우측을 계산한다.

 

2~3에 해당하는 넓이 4*2 = 8 계산. 5~6에 해당하는 넓이 3*2 = 6 계산.

남은 범위 계산을 위해 재귀호출을 이어간다.

 

3~3 범위 1*5 = 5 계산. 6~6 범위 3*1 = 3 계산.

더 이상 탐색할 범위가 없으므로 앞서 나온 모든 값들 중 최대값 8을 출력하면 정답이다.

 

정답 코드

import Foundation
let N = Int(readLine()!)!
var arr = [Int]()
var tree = Array(repeating: 0, count: N*4)
var ans = 0
for _ in 0..<N{
arr.append(Int(readLine()!)!)
}
func init_tree(start:Int,end:Int,node:Int)->Int{
if start == end{
tree[node] = start
return tree[node]
}
let mid = (start+end)/2
let left = init_tree(start: start, end: mid, node: node*2)
let right = init_tree(start: mid+1, end: end, node: node*2+1)
if arr[left] == arr[right]{
tree[node] = left<right ? left:right
}else{
tree[node] = arr[left]<arr[right] ? left:right
}
return tree[node]
}
func read_idx(start:Int, end:Int, node:Int, from:Int, to:Int)->Int{
if end < from || to < start{
return -1
}
if from <= start && end <= to{
return tree[node]
}
let mid = (start+end)/2
let left = read_idx(start: start, end: mid, node: node*2, from: from, to: to)
let right = read_idx(start: mid+1, end: end, node: node*2+1, from: from, to: to)
if left<0 {
return right
}else if right < 0{
return left
}else{
if arr[left] == arr[right]{
return left<right ? left:right
}else{
return arr[left]<arr[right] ? left:right
}
}
}
func update_ans(from:Int, to:Int){
let idx = read_idx(start: 0, end: N-1, node: 1, from: from, to: to)
let height = arr[idx]
let width = to-from+1
ans = max(ans, height*width)
if from == to{ return }
if from < idx{ update_ans(from: from, to: idx-1) }
if idx < to{ update_ans(from: idx+1, to: to) }
}
init_tree(start: 0, end: N-1, node: 1)
update_ans(from: 0, to: N-1)
print(ans)
view raw 1725.swift hosted with ❤ by GitHub

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

[7578] 공장  (0) 2023.03.11
[1280] 나무 심기  (1) 2023.03.09
[1849] 순열  (0) 2023.03.05
[1517] 버블 소트  (0) 2023.03.02
[1572] 중앙값  (0) 2023.03.01

 

https://www.acmicpc.net/problem/1849

 

1849번: 순열

1부터 N까지의 수들이 한 번씩 쓰인 수열이 있다. 그 수열에서 i 앞에 있는 수 들 중, i보다 큰 수들의 개수를 A[i]라고 정의하자. A[i]가 주어져 있을 때, 원래 수열을 구하는 프로그램을 작성하여라

www.acmicpc.net

세그먼트 트리 문제다.

 

풀이

숫자 i 앞에 i보다 큰 숫자의 개수는 작은 수부터 탐색했을 때, 앞에 주어진 빈칸의 개수로 접근 가능하다. 트리의 리프가 1인 구간합 트리를 생성하여 해당 숫자가 몇 번째 숫자인지 뽑아내면 문제를 해결할 수 있게 된다. 수열 [2 4 3 1]의 경우로 시나리오를 작성하면 다음과 같다.

 

입력 예제 : 3 0 1 0

수열의 구간중 남아있는 공간의 수를 나타내는 세그먼트 트리 생성.

트리 초기화 및 입력값 저장. 순번으로 다루기 편하게 입력값을 +1 처리하여 저장한다.

 

가장 먼저 1의 인덱스를 찾는다. 남아 있는 공간 중 네 번째 공간의 인덱스를 탐색. 3이 반환된다. 해당 인덱스에 1 저장.

다음 탐색을 위해 방문했던 노드는 -1 처리한다.

 

2의 인덱스 탐색, 현재 남아있는 공간중 첫 번째 공간의 인덱스로 0이 반환된다.

 

3의 인덱스를 탐색, 현재 남아있는 공간중 두 번째 공간의 인덱스인 2가 반환된다

 

마지막으로 4의 인덱스 탐색. 남아있는 공간중 첫 번째 공간, 즉 유일하게 남아있는 공간의 인덱스를 반환하여 수열을 완성하면 된다.

 

 

정답 코드

import Foundation
let N = Int(readLine()!)!
var ans = Array(repeating: "", count: N)
var arr = [Int]()
var tree = Array(repeating: 0, count: N*4)
for _ in 0..<N{
arr.append(Int(readLine()!)!+1)
}
func init_tree(from:Int, to:Int, idx:Int) ->Int{
if from == to{
tree[idx] = 1
return tree[idx]
}
let mid = (from+to)/2
let left = init_tree(from: from, to: mid, idx: idx*2)
let right = init_tree(from: mid+1, to: to, idx: idx*2+1)
tree[idx] = left + right
return tree[idx]
}
func read_idx(from:Int, to:Int, idx:Int, count:Int) -> Int{
tree[idx] -= 1
if from == to{
return from
}
let mid = (from+to)/2
let left = tree[idx*2]
if left >= count{
return read_idx(from: from, to: mid, idx: idx*2, count: count)
}else{
return read_idx(from: mid+1, to: to, idx: idx*2+1, count: count-left)
}
}
init_tree(from: 0, to: N-1, idx: 1)
for i in 0..<N{
let count = arr[i]
let idx = read_idx(from: 0, to: N-1, idx: 1, count: count)
ans[idx] = "\(i+1)"
}
print(ans.joined(separator: "\n"))
view raw 1849.swift hosted with ❤ by GitHub

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

[1280] 나무 심기  (1) 2023.03.09
[1725] 히스토그램  (0) 2023.03.07
[1517] 버블 소트  (0) 2023.03.02
[1572] 중앙값  (0) 2023.03.01
[1321] 군인  (0) 2023.02.28

https://www.acmicpc.net/problem/1517

 

1517번: 버블 소트

첫째 줄에 N(1 ≤ N ≤ 500,000)이 주어진다. 다음 줄에는 N개의 정수로 A[1], A[2], …, A[N]이 주어진다. 각각의 A[i]는 0 ≤ |A[i]| ≤ 1,000,000,000의 범위에 들어있다.

www.acmicpc.net

제목은 버블 소트지만 크게 세그먼트트리 또는 머지 소트로 접근하는 문제다. 세그먼트 트리를 공부하기 위해 세그먼트 트리로 접근했다.

 

풀이

버블 소트는 자신보다 뒷순번의 숫자 중 더 작은 숫자와 swap이 일어나게 된다. 즉 자신보다 뒷순번의 숫자 중 더 작은 수의 개수를 세그먼트 트리를 통해 구해내고 그 수의 합을 반환하면 문제에서 요구하는 swap이 일어나는 수를 구할 수 있다는 얘기다.

 

입력된 수열에 숫자와 인덱스가 담긴 튜플배열을 생성하여 숫자 기준 오름차순으로 정렬한다. 0으로 초기화된 세그먼트트리를 생성. 이후 (숫자, 인덱스) 튜플이 담긴 배열을 순회하면서 (인덱스 ~ N-1) 구간의 숫자를 정답에 더해준 뒤 해당 구간에 +1 갱신해 준다. 수열을 모두 돌게 되면 자연스럽게 자신보다 뒷구간에 더 작은 숫자들의 개수를 계산할 수 있게 된다.

 

예제 [3,2,1]을 기준으로 설명하면 다음과 같다.

구간 합 세그먼트 트리 생성. (숫자, 인덱스) 튜플을 숫자 기준 오름차순으로 정렬한다.

 

1의 인덱스는 2였다. 2부터 마지막 인덱스 2 즉 2~2 구간의 값을 조회. 0을 정답에 추가.

이후 들어올 구간합을 계산하기 위해 2-2에 +1 갱신작업을 한다.

 

다음 숫자의 인덱스는 1이다. 1~2구간의 값을 조회. 1이 반환되며 해당 결과를 정답에 저장. 

이후 마찬가지로 1-1 구간에 +1 갱신 작업을 한다.

 

마지막 3의 인덱스는 0이다. 0~2 구간의 값을 조회. 3보다 뒤에 있으면서 작은 숫자들의 개수는 2이다. 정답에 저장한다.

0-0 구간에 +1 갱신. 정답에는 0 + 1 + 2 즉 3이 담겨있고 해당 값이 swap이 일어난 횟수이다.

 

정답 코드

import Foundation
let N = Int(readLine()!)!
let num = readLine()!.split(separator: " ").map{Int($0)!}
var arr = [(value:Int,idx:Int)]()
var tree = Array(repeating: 0, count: N*4)
var ans = 0
for i in 0..<N{
arr.append((num[i],i))
}
arr.sort(by:{ $0.value < $1.value })
func update_tree(from:Int, to:Int, node:Int, target_idx:Int, value:Int){
if target_idx < from || to < target_idx{
return
}
tree[node] += value
if from == to{
return
}
let mid = (from+to)/2
update_tree(from: from, to: mid, node: node*2, target_idx: target_idx, value: value)
update_tree(from: mid+1, to: to, node: node*2+1, target_idx: target_idx, value: value)
}
func read_tree(from:Int, to:Int, node:Int, min:Int, max:Int) -> Int{
if to < min || max < from{
return 0
}
if min <= from && to <= max{
return tree[node]
}
let mid = (from+to)/2
let left = read_tree(from: from, to: mid, node: node*2, min: min, max: max)
let right = read_tree(from: mid+1, to: to, node: node*2+1, min: min, max: max)
return left + right
}
for num in arr{
ans += read_tree(from: 0, to: N-1, node: 1, min: num.idx, max: N-1)
update_tree(from: 0, to: N-1, node: 1, target_idx: num.idx, value: 1)
}
print(ans)
view raw 1517.swift hosted with ❤ by GitHub

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

[1725] 히스토그램  (0) 2023.03.07
[1849] 순열  (0) 2023.03.05
[1572] 중앙값  (0) 2023.03.01
[1321] 군인  (0) 2023.02.28
[2243] 사탕상자  (0) 2023.02.28

https://www.acmicpc.net/problem/1572

 

1572번: 중앙값

중앙값이란, 수열을 정렬했고, 그 크기가 N일 때, 1부터 시작해서 (N+1)/2번째 있는 원소가 그 수열의 중앙값이다. 예를 들어, {1, 2, 6, 5, 4, 3}에서는 3이고, {11, 13, 12, 15, 14}에서는 13이다. 오세준은 1

www.acmicpc.net

세그먼트 트리 문제다.

 

풀이

[1321] 군인 문제와 [2243] 사탕상자 문제와 마찬가지로 최근 K개의 숫자 중 (K+1)/2 번째 숫자에 접근하면 된다. 0부터 65536의 숫자가 몇 개가 담겨있는지를 표현하는 구간 합 세그먼트트리를 생성. 이후 N개의 입력을 받으면서 세그먼트트리에 갱신해 준 뒤, 원소가 K개 이상일 때부터 (K+1)/2 번째로 작은 숫자를 계산하면 정답을 구할 수 있다.

 

여기서 중요한 점은 최근 K개까지가 범위이므로 중앙값을 반환할 때마다 (수열의 크기 - K) 번째 숫자를 세그먼트트리에서 하나씩 제거해주어야 한다.

 

정답 코드

import Foundation
let max = 65537
let NK = readLine()!.split(separator: " ").map{Int($0)!}
let N = NK[0]
let K = NK[1]
var ans = 0
var arr = [Int]()
var tree = Array(repeating: 0, count: max*4)
func update_tree(from:Int, to:Int, idx:Int, target_idx:Int, value:Int){
if target_idx < from || to < target_idx{
return
}
tree[idx] += value
if from == to{
return
}
let mid = (from+to)/2
update_tree(from: from, to: mid, idx: idx*2, target_idx: target_idx, value: value)
update_tree(from: mid+1, to: to, idx: idx*2+1, target_idx: target_idx, value: value)
}
func read_tree(from:Int, to:Int, idx:Int, num:Int) -> Int{
if from == to{
return from
}
let mid = (from+to)/2
let left = tree[idx*2]
if left >= num{
return read_tree(from: from, to: mid, idx: idx*2, num: num)
}else{
return read_tree(from: mid+1, to: to, idx: idx*2+1, num: num-left)
}
}
var pre = 0
for i in 1...N{
let num = Int(readLine()!)!
arr.append(num)
update_tree(from: 0, to: max-1, idx: 1, target_idx: num, value: 1)
if i >= K{
ans += read_tree(from: 0, to: max-1, idx: 1, num: (K+1)/2)
update_tree(from: 0, to: max-1, idx: 1, target_idx: arr[pre], value: -1)
pre += 1
}
}
print(ans)
view raw 1572.swift hosted with ❤ by GitHub

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

[1849] 순열  (0) 2023.03.05
[1517] 버블 소트  (0) 2023.03.02
[1321] 군인  (0) 2023.02.28
[2243] 사탕상자  (0) 2023.02.28
[3745] 오름세  (0) 2023.02.26

+ Recent posts