問題
$N$ 個の点 $P_1, \ldots, P_N$($x$ 昇順 $y$ 昇順にソート済み)と $M$ 個の点 $Q_1, \ldots, Q_M$(同じく $x$ 昇順 $y$ 昇順)が与えられる。
各 $Q_j$ に対し、マンハッタン距離 $|x_{Q_j} - x_{P_i}| + |y_{Q_j} - y_{P_i}|$ が最小となる $P_i$ への距離を求めよ。
ただし $P, Q$ ともに $x, y$ 座標が単調増加する(コスト行列が完全単調)という特殊条件下では、分割統治最適化で全体 $O((N+M)\log N)$ が実現できる。
制約
| パラメータ | 範囲 |
|---|---|
| $N, M$ | $1 \le N, M \le 2 \times 10^5$ |
| 座標 | $-10^9 \le x, y \le 10^9$(整数) |
| 単調条件 | $P$ は $x$ 昇順かつ $y$ 昇順、$Q$ も同様 |
入出力例
入力例 1
4 3
1 2
3 5
6 7
9 10
2 3
5 6
8 9
出力例 1
1
1
1
入力例 2
3 3
0 0
5 5
10 10
1 1
6 4
9 11
出力例 2
0
2
1
概念図: 分割統治最適化の単調性利用
ヒント(段階的開示)
ヒント1: 方向性
$P, Q$ ともに $x, y$ が単調増加のとき、コスト行列 $C[j][i] = \mathrm{dist}(Q_j, P_i)$ は完全単調(totally monotone)になり、各行の最小値インデックスが単調増加する。これを分割統治で利用する。
ヒント2: アプローチ
分割統治最適化:
- クエリ範囲 $[ql, qr]$ の中央 $qmid$ を選ぶ
- $P[pl..pr]$ を線形走査して $Q[qmid]$ の最近点 $best\_i$ を見つける
- 左再帰: $dc(ql, qmid-1, pl, best\_i)$(P の右端を $best\_i$ で制限)
- 右再帰: $dc(qmid+1, qr, best\_i, pr)$(P の左端を $best\_i$ から開始)
ヒント3: コード骨格
def dc(ql, qr, pl, pr):
if ql > qr: return
qmid = (ql + qr) // 2
best_i, best_d = pl, float('inf')
for i in range(pl, pr + 1):
d = manhattan(P[i], Q[qmid])
if d < best_d:
best_d = d; best_i = i
result[qmid] = best_d
dc(ql, qmid - 1, pl, best_i)
dc(qmid + 1, qr, best_i, pr)
dc(0, M - 1, 0, N - 1)
模範解答 (Python)
import sys
def solve():
data = sys.stdin.read().split()
idx = 0
N = int(data[idx]); idx += 1
M = int(data[idx]); idx += 1
P = []
for _ in range(N):
x = int(data[idx]); idx += 1
y = int(data[idx]); idx += 1
P.append((x, y))
Q = []
for _ in range(M):
x = int(data[idx]); idx += 1
y = int(data[idx]); idx += 1
Q.append((x, y))
def manhattan(p, q):
return abs(p[0] - q[0]) + abs(p[1] - q[1])
result = [0] * M
def dc(ql, qr, pl, pr):
if ql > qr:
return
qmid = (ql + qr) // 2
best_i = pl
best_d = float('inf')
for i in range(pl, pr + 1):
d = manhattan(P[i], Q[qmid])
if d < best_d:
best_d = d
best_i = i
result[qmid] = best_d
dc(ql, qmid - 1, pl, best_i)
dc(qmid + 1, qr, best_i, pr)
dc(0, M - 1, 0, N - 1)
print(*result, sep='\n')
solve()
Step-by-Step 解説
Step 1: 単調性の証明
$P, Q$ ともに $x, y$ が単調増加のとき、$Q_j$ に対する最近 $P_i$ のインデックス $\mathrm{opt}(j)$ は $j$ が増えると単調増加する(コスト行列の逆三角不等式から証明)。
Step 2: 分割統治最適化の構造
単調性を利用して探索範囲を絞る。各レベルで P 範囲の和が $O(N)$、Q 範囲の和が $O(M)$ → 全体 $O((N+M)\log M)$。
Step 3: マンハッタン距離の場合の単調性
$x, y$ 共に単調増加なら $\mathrm{dist}(Q_j, P_i)$ のコスト行列は inverse Monge 行列になり、各行最小値インデックスが単調増加する。
Step 4: SMAWK との関係
SMAWK アルゴリズムは完全単調行列の全行最小値を $O(N+M)$ で求める。分割統治版は $O((N+M)\log)$ だが実装が簡単。
Step 5: 計算量比較
| 手法 | 計算量 | 実装難易度 |
|---|---|---|
| ナイーブ | $O(NM)$ | 易 |
| 分割統治最適化 | $O((N+M)\log M)$ | 中 |
| SMAWK | $O(N+M)$ | 難 |
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
右再帰の pl を best_i+1 にする | best_i は右側でも使える可能性がある | dc(qmid+1, qr, best_i, pr) |
| 単調性の前提を確認せず適用 | 任意入力では誤答 | 問題の単調条件を必ず確認 |
| 再帰深度が Python 制限を超える | $M = 2 \times 10^5$ で深さ $\log_2 M \approx 17$ なので問題なし | 必要なら sys.setrecursionlimit |
| SMAWK と分割統治の混同 | SMAWK は $O(N+M)$、分割統治は $O((N+M)\log)$ | 問題サイズに応じて選択 |
次のステップ
- 発展問題: 完全単調行列の SMAWK 実装($O(N+M)$ を実現)
- 1D-1D DP 最適化(Knuth 最適化・四辺形不等式)との統一理解
- 並列二分探索との組み合わせ
- $k$-nearest neighbor を分割統治で解く