BOJ_17435_합성함수와 쿼리 (Java)
BOJ_17435_합성함수와 쿼리 (Java)
[Gold I] 합성함수와 쿼리 - 17435
성능 요약
메모리: 103124 KB, 시간: 900 ms
분류
자료 구조, 희소 배열
제출 일자
2025년 2월 5일 05:07:06
문제 설명
함수 f : {1, 2, ..., m}→{1, 2, ..., m}이 있다. 이때 fn : {1, 2, ..., m}→{1, 2, ..., m}을 다음과 같이 정의하자.
- f1(x) = f(x)
- fn+1(x) = f(fn(x))
예를 들어 f4(1) = f(f(f(f(1))))이다.
n과 x가 주어질 때 fn(x)를 계산하는 쿼리를 수행하는 프로그램을 작성하시오.
입력
첫 줄에 정수 m이 주어진다. (1 ≤ m ≤ 200,000)
다음 줄에 f(1), f(2), ..., f(m)이 차례대로 주어진다.
다음 줄에 쿼리의 개수 Q가 주어진다. (1 ≤ Q ≤ 200,000)
다음 Q개의 줄에 각각 정수 n과 x가 주어진다. (1 ≤ n ≤ 500,000; 1 ≤ x ≤ m)
출력
주어지는 n, x마다 fn(x)를 출력한다.
문제 풀이
희소 배열이란?
2의 거듭제곱 단위로 건너뛰는 결과를 미리 저장해두는 배열입니다 dp[k][x] = x에서 시작해서 2^k번 이동했을 때의 도착점
예시로 이해하기
1
f = [3, 3, 5, 4, 3] (인덱스는 1부터 시작)
-
dp[0][x] = 한 번 이동
- dp[0][1] = 3 (1→3)
- dp[0][2] = 3 (2→3)
- dp[0][3] = 5 (3→5)
-
dp[1][x] = 두 번 이동
- dp[1][1] = 5 (1→3→5)
- dp[1][2] = 5 (2→3→5)
- dp[1][3] = 3 (3→5→3)
-
p[2][x] = 네 번 이동
- dp[2][1] = 3 (1→3→5→3→5)
이런 식으로 2의 거듭제곱 단위로 몇 번 이동했을 때의 결과를 미리 계산해두는 것이 희소 배열의 핵심입니다.
장점
- n번 이동해야 할 때, n을 이진수로 분해해서 필요한 부분만 사용
- 예: 7번 이동 = 4번 이동 + 2번 이동 + 1번 이동 (7 = 4 + 2 + 1)
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* Author: nowalex322, Kim HyeonJae
*/
import java.io.*;
import java.util.*;
public class Main {
static BufferedReader br;
static BufferedWriter bw;
static StringTokenizer st;
static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws Exception {
new Main().solution();
}
public void solution() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
// br = new BufferedReader(new InputStreamReader(new FileInputStream("src/main/java/BOJ_17435_합성함수와쿼리/input.txt")));
bw = new BufferedWriter(new OutputStreamWriter(System.out));
int m = Integer.parseInt(br.readLine());
int[][] dp = new int[19][m+1]; // 2^18 > 500000 이므로 19도 충분함
st = new StringTokenizer(br.readLine());
for(int i = 1; i <= m; i++) {
dp[0][i] = Integer.parseInt(st.nextToken());
}
for(int i=1; i<19; i++) {
for(int j=1; j<=m; j++){
dp[i][j] = dp[i-1][dp[i-1][j]];
}
}
int q = Integer.parseInt(br.readLine());
while(q--> 0) {
st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int x = Integer.parseInt(st.nextToken());
for(int i=0; i<19; i++){
if((n & (1<<i)) != 0) x = dp[i][x];
}
sb.append(x).append("\n");
}
bw.write(sb.toString());
bw.flush();
bw.close();
br.close();
}
}
입출력 가장빠르게 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* Author: nowalex322, Kim HyeonJae
*/
import java.io.*;
import java.util.*;
public class Main {
static StringBuilder sb = new StringBuilder();
static int m, q, n, x;
public static void main(String[] args) throws Exception {
new Main().solution();
}
public void solution() throws Exception {
m = read();
int[][] dp = new int[19][m+1];
// f(x) 입력 받기
for(int i = 1; i <= m; i++) {
dp[0][i] = read();
}
// 희소 배열 계산
for(int i=1; i<19; i++) {
for(int j=1; j<=m; j++){
dp[i][j] = dp[i-1][dp[i-1][j]];
}
}
q = read();
while(q--> 0) {
n = read();
x = read();
for(int i=0; i<19; i++){
if((n & (1<<i)) != 0) x = dp[i][x];
}
sb.append(x).append("\n");
}
System.out.println(sb);
}
public static int read() throws IOException {
int n = 0;
boolean isNegative = false;
int c;
while ((c = System.in.read()) <= 32) ;
if (c == '-') {
isNegative = true;
c = System.in.read();
}
do n = (n << 3) + (n << 1) + (c & 15);
while ((c = System.in.read()) > 32);
return isNegative ? -n : n;
}
}
이정도 차이난다.
This post is licensed under
CC BY 4.0
by the author.