Mo's Algorithm

tktj12·2024년 4월 6일
0

오프라인 쿼리를 제곱근 분할법을 사용하여 빠르게 처리하는 알고리즘.

제약 조건


크기가 NN인 입력 배열 ArrArrQQ개의 쿼리가 있다. 각 쿼리는 [L,R][L, R]로 표현된다. (LRL \le R)

  1. ArrArr은 쿼리에 의해 변경되지 않고, 모든 쿼리들은 사전에 정보가 주어진다. (오프라인 쿼리)
  2. [L,R][L,R]을 계산한 이후, 범위가 1만큼 차이나는 쿼리(e.g. [L+1,R],[L,R1][L+1,R], [L, R-1])들을 일정 시간(e.g. O(F)O(F))에 구한다.


정의


  1. 모든 인덱스는 0부터 시작한다.
  2. BLOCK_SIZE=NBLOCK\_SIZE = \lfloor\sqrt{N}\rfloor
  3. 어떤 쿼리 [L,R][L, R]L/BLOCK_SIZE\lfloor L / BLOCK\_SIZE \rfloor번째 블록에 속한다.
  4. 같은 블록에서는 RR이 작을 수록 우선순위가 높다. 즉, [4,8]보다 [4,6]이 먼저 처리된다.
  5. 블록의 개수 k=N/BLOCK_SIZE k = \lceil N/BLOCK\_SIZE \ \rceil
  6. BiB_i : ii번 블록.
  7. Bi|B_i| : ii번 블록에 속한 쿼리의 개수


시간 복잡도


O(QlogQ+(N+Q)NF)O(Q\log Q + (N+Q)\sqrt{N}F)

모스 알고리즘은 B0B_0부터 Bk1B_{k-1}까지 우선 순위에 따라 쿼리들을 처리한다. 따라서 각 블록들을 우선순위에 따라 정렬한다 : O(QlogQ)O(Q\log Q)

모스 알고리즘은 이전에 처리한 쿼리의 범위를 활용한다. 즉, 좌측 값과 우측 값을 최소한으로 증감하여 쿼리들을 처리한다. 좌/우측 값이 총 O((N+Q)N)O((N+Q)\sqrt{N})만큼 변화하고, 좌/우측값이 1 변화할 때마다 O(F)O(F)시간이 소요된다 :
O((N+Q)NF)O((N+Q)\sqrt{N}F)

우측 값(RR)의 변화

  1. 블록 안에서

    한 블록에서 RR은 증가하기만 한다. RRNN보다 커질 수 없다. 따라서 한 블록에서 RRO(N)O(N)만큼 바뀐다. 블록은 O(N)O(\sqrt{N})개 있으므로, 총 O(NN)O(N\sqrt{N})으로 계산할 수 있다.

  2. 블록이 바뀔 때

    블록이 바뀌는 순간에 RR은 최대 N1N-1만큼 바뀐다. 블록은 O(N)O(\sqrt{N})번 바뀌므로, 총 O(NN)O(N\sqrt{N})으로 계산할 수 있다.

알고리즘 수행 중 우측 값의 변화는 1, 2를 합쳐 총 O(NN)O(N\sqrt{N})만큼이다.

좌측 값(LL)의 변화

  1. 블록 안에서

    같은 블록에서 LL은 최대 N\sqrt{N}만큼 차이난다. 따라서 어떤 블록 BiB_i에서 LLO(BiN)O(|B_i| * \sqrt{N})만큼 바뀐다.
    B0+B1+...+Bk1=Q|B_0| + |B_1| + ... + |B_{k-1}| = Q이므로, 총 O(QN)O(Q\sqrt{N})으로 계산할 수 있다.

  2. 블록이 바뀔 때

    어떤 블록 BiB_i에서 다른 블록 Bi+xB_{i+x} (x>0)(x > 0)로 갈 때 LLO(xBLOCK_SIZE)O(x * BLOCK\_SIZE)만큼 바뀐다. 블록이 kk개 있으므로 모든 xx의 합이 kk를 넘을 수 없다. 따라서 총 O(kN)=O(NN)=O(N)O(k*\sqrt{N}) = O(\sqrt{N} * \sqrt{N}) = O(N)으로 계산할 수 있다.

알고리즘 수행 중 좌측 값의 변화는 1, 2를 합쳐 총 O(QN+N)O(Q\sqrt{N} + N)이다.



연습문제



참고 자료

https://www.hackerearth.com/practice/notes/mos-algorithm/

profile
C++, 알고리즘 공부

0개의 댓글