Β - Last update: 2023-10-29

개인적으둜 κ³΅λΆ€ν•˜λ‹€κ°€ κ·Έ μ‹¬ν”Œν•¨κ³Ό λ°œμƒμ— 좩격먹은 μ•Œκ³ λ¦¬μ¦˜ 1μˆœμœ„ (2023/07 κΈ°μ€€)

Mo's Algorithm을 λ‚΄κ°€ μ΄ν•΄ν•œ μˆœμ„œλŒ€λ‘œ ν•œλ²ˆ μ •λ¦¬ν•΄λ³΄κ³ μž ν•œλ‹€.

TL;DR

Offline Queryκ°€ κ°€λŠ₯ν•΄μ•Ό 함, μ•„λž˜μ™€ 같이 쿼리 μ •λ ¬

  • sqrtN=NsqrtN = \sqrt N
struct Q { // Query
int s, e;
int oidx; // Original Index
bool operator<(const struct Q& t) const {
if (e / sqrtN == t.e / sqrtN) return s < t.s;
return e / sqrtN < t.e / sqrtN;
}
};

Bucket Challenge

λ‚œ 개인적으둜 bucket을 ν™œμš©ν•œ 풀이λ₯Ό 맀우 μ’‹μ•„ν•œλ‹€. μ•Œκ³ λ¦¬μ¦˜ λͺ…μœΌλ‘œλŠ” Sqrt Decomposition으둜 많이 λΆ€λ₯΄λŠ” 기법인데, 전체 λ°°μ—΄ 크기가 NN μΌλ•Œ, 전체 ꡬ간을 N\sqrt N 으둜 λ‚˜λˆ„κ²Œ 되면, 각 bucket의 크기 λ˜ν•œ N\sqrt N이 λœλ‹€. μ΄λ ‡κ²Œ 되면, 각 κ΅¬κ°„λ§ˆλ‹€μ˜ 계산값(이λ₯Όν…Œλ©΄ κ΅¬κ°„λ§ˆλ‹€μ˜ ν•©)을 κ΅¬ν•˜λŠ” 데에 μ΅œλŒ€ O(N)O(\sqrt N)의 μ‹œκ°„μ΄ 걸리게 λ˜λŠ” μ•„λ¦„λ‹€μš΄ 결둠에 λ„λ‹¬ν•œλ‹€.

예λ₯Ό λ“€μ–΄, 전체 ꡬ간 크기가 10000이면, 각 ꡬ간은 100으둜 λ‚˜λˆŒ 수 있고, 이 μƒνƒœμ—μ„œ 각 κ΅¬κ°„μ˜ 합을 bucket[i/100]에 미리 계산해 λ‘μ—ˆλ‹€κ³  ν•˜μž. 그러면, [4,303][4, 303] κ΅¬κ°„μ˜ 합은 미리 계산해둔 값듀을 λ°”νƒ•μœΌλ‘œ μ•„λž˜μ²˜λŸΌ κ΅¬ν•˜λ©΄ λœλ‹€.

  • A[4], A[5], A[6], ... A[99]
  • bucket[1] (100 ~ 199의 합이 미리 κ³„μ‚°λ˜μ–΄ μžˆλ‹€)
  • bucket[2] (200 ~ 299의 합이 미리 κ³„μ‚°λ˜μ–΄ μžˆλ‹€)
  • A[300], A[301], ... A[303]

μœ„μ—μ„œ 확인이 κ°€λŠ₯ν•œ κ²ƒμ²˜λŸΌ, bucket μ™Έμ˜ ꡬ간을 ν•©ν•΄μ•Ό ν•˜λŠ” μˆ˜λŠ” worst의 κ²½μš°μ—λ„ N\sqrt N을 λ„˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ 이런 경우 query λ§ˆλ‹€μ˜ μ‹œκ°„ λ³΅μž‘λ„λŠ” μ΅œλŒ€ O(N)O(\sqrt N)이 λœλ‹€κ³  ν•  수 μžˆλŠ” 것이닀.

Mo's Algorithm

λ°”λ‘œ μ΄λŸ¬ν•œ 루트둜 λ‚˜λˆ„λŠ” 아이디어λ₯Ό Offline query에 μ μš©ν•œ 것이 Mo's Algorithm이닀. Offline query μ—­μ‹œ λ°œμƒμ΄ λ…νŠΉν•œλ°, queryλ₯Ό κΌ­ μž…λ ₯이 λ“€μ–΄μ˜¨ μˆœμ„œλŒ€λ‘œ μ²˜λ¦¬ν•˜μ§€ μ•ŠλŠ” 것을 λ§ν•œλ‹€. λ§Œμ•½ μ•„λž˜μ™€ 같은 queryκ°€ λ°˜λ³΅λœλ‹€κ³  ν•΄λ³΄μž.

  • [1,100][1, 100]
  • [1000,1300][1000, 1300]
  • [2,101][2, 101]
  • [1001,1301][1001, 1301]
  • [3,102][3, 102]
  • [1002,1302][1002, 1302]

λ”± 봐도 queryλ₯Ό μž¬λ°°μΉ˜ν•˜κ³  싢은 μš•κ΅¬κ°€ 생길 것이닀. μž¬λ°°μΉ˜ν•˜λ©΄ 이전에 κ³„μ‚°ν•œ 값을 ν™œμš©ν•  여지가 있기 λ•Œλ¬Έμ΄λ‹€. λ‹€λ§Œ, 재배치λ₯Ό 일반적으둜 μ–΄λ–€ μˆœμ„œλ‘œ ν•  것인지가 λ§‰λ§‰ν•˜λ‹€. Sqrt DecompositionκΉŒμ§€λŠ” 루트λ₯Ό μ–΄λ–»κ²Œ ν™œμš©ν• μ§€μ— λŒ€ν•œ 아이디어가 μ–΄λŠ 정도 μ§κ΄€μœΌλ‘œ μ°Ύμ•„μ§€μ§€λ§Œ, Mo's 정도 되면 쉽지 μ•Šλ‹€. 재배치 방식에 λŒ€ν•΄ μ•„λž˜μ²˜λŸΌ 생각을 ν•΄λ³΄λŠ” 것이 일반적일 것이닀.

  1. [s,e][s, e]에 λŒ€ν•΄μ„œ s λ¨Όμ € μ •λ ¬ν•˜λ©΄?
    • [1,100][1, 100], [1,10000][1, 10000], [2,100][2, 100], [2,10000][2, 10000] 처럼 μ§ˆμ˜κ°€ 올 수 μžˆλ‹€.
  2. [s,e][s, e]에 λŒ€ν•΄μ„œ e λ¨Όμ € μ •λ ¬ν•˜λ©΄?
    • 1κ³Ό 같은 λ°˜λ‘€κ°€ μ‘΄μž¬ν•œλ‹€.
  3. [s,e][s, e]에 λŒ€ν•΄μ„œ s λ¨Όμ € μ •λ ¬ν•˜κ³  κ°™μœΌλ©΄ e둜 μ •λ ¬ν•˜λ©΄?
    • κ·ΈλŸ΄μ‹Έ ν•˜μ§€λ§Œ, μ „μ²΄μ μœΌλ‘œ λΉ„νš¨μœ¨μ΄ μ‘΄μž¬ν•˜λŠ” 것은 λ§ˆμ°¬κ°€μ§€μ΄λ‹€.
    • μ•„λž˜ μΌ€μ΄μŠ€κ°€ μ—¬μ „νžˆ 해결이 μ•ˆλœλ‹€.
    • [1,2][1, 2], [1,10000][1, 10000], [2,3][2, 3], [2,10001][2, 10001], ...

즉, μœ„μ²˜λŸΌ λ‹¨μˆœ μ •λ ¬ ν•΄μ„œλŠ” μ–΄λ–»κ²Œ 정렬해도 쉽지 μ•Šλ‹€. Mo's Algorithm은 μ—„μ²­λ‚œ λ°œμƒμ—μ„œ μΆœλ°œν•˜λŠ”λ°, λ°”λ‘œ ν•œμͺ½ ꡬ간에 루트λ₯Ό μ·¨ν•˜λŠ” 것이닀. μ•„λž˜μ™€ 같은 operatorλ₯Ό 톡해 μ •λ ¬ν•˜λŠ” 것을 μƒκ°ν•΄λ³΄μž.

bool operator<(const struct Q& t) const {
if (e / sqrtN == t.e / sqrtN) return s < t.s;
return e / sqrtN < t.e / sqrtN;
}

e\sqrt eκ°€ 같을 λ•Œ ssλ₯Ό ν™œμš©ν•΄ μ˜€λ¦„μ°¨μˆœμœΌλ‘œ ν•˜κ³ , λ‹€λ₯Ό λ•ŒλŠ” e\sqrt eκ°€ μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬ν•˜λŠ” 것이닀. μ΄λ ‡κ²Œ ν•˜λ©΄, 처음의 μ˜ˆμ‹œκ°€ μ•„λž˜μ²˜λŸΌ κΉ”λ”ν•˜κ²Œ 떨어진닀. (N=10000N = 10000을 κ°€μ •ν•˜μž)

  • [1,100][1, 100] : eκ°€ bucket 1에 μ†ν•˜λ―€λ‘œ 제일 λ¨Όμ € μ˜¨λ‹€.
  • [2,101][2, 101] : μœ„μ™€ λ™μΌν•œ bucket이고, s의 μˆœμ„œμ— 따라 μ •λ ¬λ˜μ—ˆλ‹€.
  • [3,102][3, 102]
  • [1000,1300][1000, 1300] : eκ°€ bucket 13에 μ†ν•˜λ―€λ‘œ 1λ²ˆλ³΄λ‹€ λ‚˜μ€‘μ— μ˜¨λ‹€.
  • [1001,1301][1001, 1301]
  • [1002,1302][1002, 1302]

μ§κ΄€μœΌλ‘œ 확인해봐도, query κ°„ 이동할 λ•Œ κ±Έλ¦¬λŠ” μ‹œκ°„μ΄ μƒλ‹Ήνžˆ κ°œμ„ μ΄ 될 것이라고 μ§μž‘ν•  수 μžˆλ‹€. s와 eκ°€ μ΄λ™ν•˜λŠ” 전체적인 거리λ₯Ό μƒκ°ν•΄λ³΄μž.

  1. eλŠ” μ „μ²΄μ μœΌλ‘œλŠ” O(N)O(N), 그리고 query ꡬ간 λ‚΄μ—μ„œ O(N)O(\sqrt N) 만큼 이동 κ°€λŠ₯ν•˜κ³ , queryμˆ˜κ°€ MM개라고 ν•˜λ©΄, 총 O(N+MN)O(N + M\sqrt N) 만큼 이동할 수 μžˆλ‹€.
  2. sλŠ” eκ°€ μ΅œλŒ€ O(N)O(\sqrt N) 만큼 μ΄λ™ν•˜λŠ” λ™μ•ˆ μ˜€λ¦„μ°¨μˆœ μ •λ ¬λ˜μ–΄ μžˆμœΌλ―€λ‘œ μ΅œλŒ€ NN만큼 이동가λŠ₯ν•˜λ‹€. 즉, 총 O(NN)O(N\sqrt N) 만큼 이동할 수 μžˆλ‹€.

1κ³Ό 2λ₯Ό λ‘˜ λ‹€ κ³ λ €ν•˜λ©΄ μ΅œμ’…μ μΈ μ‹œκ°„ λ³΅μž‘λ„μΈ O((N+M)N)O((N + M)\sqrt N)이 λ‚˜μ˜¨λ‹€.

Time Complexity

  • ꡬ간 길이 N이고, 쿼리 M개 일 λ•Œ,
    • O((N+M)N)O((N+M) \sqrt N)

ν•˜λ‚˜μ˜ queryλ₯Ό μˆ˜ν–‰ν•˜λŠ” 데에 κ±Έλ¦¬λŠ” μ‹œκ°„μ΄ O(1)O(1)이 κ±Έλ¦°λ‹€λ©΄, 전체적인 μ‹œκ°„λ³΅μž‘λ„λŠ” μœ„μ—μ„œ μ‚΄νŽ΄λ³Έ κ²ƒμ²˜λŸΌ O((N+M)N)O((N + M)\sqrt N)이 λœλ‹€. naiveν•˜κ²Œ 전체 queryλ₯Ό μ²˜λ¦¬ν–ˆλ‹€λ©΄ O(NM)O(NM)이 걸렸을 κ²ƒμ΄λ―€λ‘œ, μ—„μ²­λ‚œ κ°œμ„ μ΄ λœλ‹€κ³  ν•  수 μžˆλ‹€.

참고 링크

🏷️ 주제 λͺ©λ‘: