알고리즘/Mo's 알고리즘

BOJ 백준 13548 수열과 쿼리 6

kyjdummy 2025. 5. 2. 17:37

 

문제 : https://www.acmicpc.net/problem/13548

[ 문제 요약 ]

  • 배열과 그 원소가 주어지고 특정 구간들이 주어집니다.
  • 구간들 사이에 가장 많이 등장하는 수가 몇 번 등장하는지 출력합니다.

 

[ 테스트 케이스 설명 ]

5		// 수열 크기 1<=100,000
1 2 1 3 3	// 1<=100,000
3		// 쿼리수 1<=100,000
1 3
2 3
1 5
// 답
2
1
2

 

[ 알고리즘 분류 ]

  • 오프라인 쿼리, Mo’s 알고리즘

 

[ 문제 해설 ]

 

위 문제는 오프라인 쿼리로 처리하면서 Mo’s 알고리즘을 이용해 쿼리를 정렬하고, 투포인터로 풀듯이 문제를 풀면 됩니다.

 

여기서 중요한 것은, 특정 수의 빈도를 체크하는 변수 cnt를 둔다면, 이 cnt가 몇 개가 있는지 추적하는 freq 배열을 따로 두어야 합니다. 빈도수의 빈도를 체크해야 max 값을 제대로 추적할 수 있습니다.

 

아래 코드는 오프라인 쿼리로 Mo’s를 활용한 정렬과, 투포인터 알고리즘을 사용한 정답 코드입니다.

 

[ 정답 코드 ]

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;
class Main{
	
	static int sqrt;
	
	static class Query implements Comparable<Query>{
		int left, right, idx;
		Query(int l, int r, int i){
			left = l;
			right=r;
			idx = i;
		}
		@Override
		public int compareTo(Query o) {
			int l = left / sqrt;
			int r = o.left / sqrt;
			return l == r ? right - o.right : l - r;
		}
	}
	
	public static void main(String[] args)throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int N		= Integer.parseInt(br.readLine());// 수열 크기 1<=100,000
		int arr[]	= new int[N + 1];
		int cnt[]	= new int[100_001];
		int freq[]	= new int[100_001];	// 빈도수가 i인 숫자의 개수
		sqrt		= (int)Math.sqrt(N);
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		for(int i=1; i<=N; i++)
			arr[i] = Integer.parseInt(st.nextToken());// 1<=100,000
		
		int Q = Integer.parseInt(br.readLine());// 쿼리수 1<=100,000
		int ans[] = new int[Q + 1];
		ArrayList<Query> query = new ArrayList<>();
		
		for(int i=1; i<=Q; i++)
		{
			st = new StringTokenizer(br.readLine());
			int l = Integer.parseInt(st.nextToken());
			int r = Integer.parseInt(st.nextToken());
			query.add(new Query(l, r, i));
		}
		
		Collections.sort(query);
		
		int idxL = 1;
		int idxR = 0;
		int max = 0;
		for(Query q : query)
		{
			while(idxR < q.right)
			{
				++idxR;
				--freq[cnt[arr[idxR]]];
				++freq[++cnt[arr[idxR]]];
				max = Math.max(max, cnt[arr[idxR]]);
			}
			
			while(q.right < idxR)
			{
				if(--freq[cnt[arr[idxR]]] == 0 && max == cnt[arr[idxR]])
					--max;

				++freq[--cnt[arr[idxR]]];
				--idxR;
			}
			
			while(idxL < q.left)
			{
				if(--freq[cnt[arr[idxL]]] == 0 && max == cnt[arr[idxL]])
					--max;

				++freq[--cnt[arr[idxL]]];
				++idxL;
			}
            
			while(q.left < idxL)
			{
				--idxL;
				--freq[cnt[arr[idxL]]];
				++freq[++cnt[arr[idxL]]];
				max = Math.max(max, cnt[arr[idxL]]);
			}

            
			ans[q.idx]= max;
		}
		
		StringBuilder sb = new StringBuilder();
		for(int i=1; i<=Q; i++)
			sb.append(ans[i]).append('\n');
		
		System.out.print(sb);
	}
}