https://www.acmicpc.net/problem/23326
도현이는 홍익 투어리스트가 되어 홍익대학교를 견학하려고 한다. 홍익대학교는 개의 구역이 원형으로 배치된 모습이다. 번 구역에서 시계 방향으로 각각 번, ... , 번 구역이 존재하고, 번 구역에서 시계 방향으로 한 칸 더 갈 경우 번 구역으로 도착한다.
홍익대학교에는 명소가 존재한다. 도현이는 알찬 투어를 위해 명소만을 방문하려 한다. 도현이는
번 구역에 서있다.
도현이를 위해 다음과 같은 쿼리를 수행하는 프로그램을 작성해보자.
: 번 구역이 명소가 아니었다면 명소로 지정되고, 명소였다면 지정이 풀리게 된다. ()
: 도현이가 시계방향으로 만큼 이동한다. ()
: 도현이가 명소에 도달하기 위해 시계방향으로 최소 몇 칸 움직여야 하는 지 출력한다. 명소가 존재하지 않는 경우 을 출력한다.
첫째 줄에 구역의 개수 ()과 쿼리의 개수 ()가 정수로 주어진다.
둘째 줄에 길이 의 수열 가 주어진다. 번째 구역이 명소라면 는 , 그렇지 않다면 이다.
셋째 줄부터 줄에 걸쳐 본문의 쿼리가 주어진다. 번 쿼리는 하나 이상 존재한다.
번 쿼리가 주어질 때마다 해당 쿼리의 값을 출력한다.
N의 크기와 Q의 크기를 명심하면서 풀이를 하였다. 결국 이 문제에서 원하는 것은 현재 도현이의 위치로 부터 가장 가까운 명소를 찾는 것인데, 무조건 시계 방향으로만 돌아야 하는 것이 제한이다. 그래서 명소를 관리하는 TreeSet 자료구조를 사용하였다. 그 이유로는 TreeSet에서는 X보다 큰 값 중에 가장 작은 값을 반환해주는 함수를 제공해주기 때문이다.
의 경우에는 TreeSet에 저장되어 있으면 삭제하고, 저장되어 있지 않으면 추가만 해주면 된다.
경우에는 도현이의 위치를 옮겨주어야 하는데 아무리 큰 수가 오더라고 결국 한 반퀴를 돌게 되면 제자리 이기 때문에 나머지를 계산해 최종적으로 움직여야 하는 칸을 계산하고 크기를 넘을 경우 N으로 나누어 주면 된다.
경우에는 우선 명소가 있는 지 체크를 한다. 그리고 현재 내 자리가 명소인지도 확인을 하고 그래도 값이 없다면 현재 위치 기준으로 마지막 장소 까지 명소를 확인하고 마지막으로 처음부터 현재 위치 기준으로 전 장소 까지 확인을 하면 된다.
그렇게 하면 전체 시간복잡도는 Q Log N이 되는데 이는 대략 100,000 18 = 1,800,000 이므로 시간제한에도 널널하다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Optional;
import java.util.StringTokenizer;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
StringBuilder sb = new StringBuilder();
TreeSet<Integer> set = new TreeSet<>();
int N = Integer.parseInt(st.nextToken());
int Q = Integer.parseInt(st.nextToken());
int pos = 1;
st = new StringTokenizer(br.readLine());
for(int i = 1 ; i <= N ;i++){
int value = Integer.parseInt(st.nextToken());
if(value == 1) {
set.add(i);
}
}
for(int i = 0; i < Q ; i++){
st = new StringTokenizer(br.readLine());
int number = Integer.parseInt(st.nextToken());
if(number == 1){
int idx = Integer.parseInt(st.nextToken());
if(set.contains(idx)){
set.remove(idx);
}else{
set.add(idx);
}
}else if(number == 2){
int move = Integer.parseInt(st.nextToken()) % N;
pos += move;
if(pos > N){
pos %= N;
}
}else{
if(set.isEmpty()){
sb.append("-1").append("\n");
continue;
}
if(set.contains(pos)){
sb.append(0).append("\n");
continue;
}
Integer front = set.higher(pos);
Integer back = set.higher(0);
if(front != null){
sb.append(front-pos).append("\n");
}else{
sb.append(N-pos+back).append("\n");
}
}
}
System.out.println(sb);
}
}