問題: E - へんなコンパス / Manhattan Compass
平面上のN個の穴について(a, b)の穴のペアから始めて、マンハッタン距離が同じ穴のペアを辿ること(ペア(p, q)とペア(p, r)の距離が等しければ、一方からもう一方を辿ることができる)で辿れるペア数を求める問題。
今回の解法は、尺取り法とUnion-Findを使う。
まず、マンハッタン距離のひし形が扱いづらいので平面を45度回転させて倍に拡大する。つまり、の変換を行う。
これで、マンハッタン距離の計算をと見なして計算できる。
この問題ではと距離が等しい穴のペアのみ辿れるため、今回は距離がと同じ穴のペアを平面を走査しながら尺取りで列挙し、のペアから辿れる穴をUnion-Findを用いて求め、辿れる穴のペア数を求める。
平面走査はxについてソートした場合とyについてソートした場合それぞれについて行う。
xについてソートした点の列の平面走査では、ある点について、, となる点を尺取りで全て列挙しながら走査する。この時列挙される点は連続した点の列となる。
そして列挙した複数の点は、点から辿れる点としてUnion-Findでuniteして管理する。
この時注意すべき点として、ある点で列挙した複数の点をuniteする際、全てのについてとuniteすると、回uniteすることになりTLEする。既にuniteした点同士はuniteする必要がないため、どの点までをuniteし終わったかを持ちながら、点と点のuniteと点と点 をuniteすることで回のuniteに抑える。
この平面走査で列挙されるペアには、もしくはとなる点のペアが2回含まれるため、この重複を除去する必要がある。
最後に、点と同じグループに属する点を含むペアの数を数える。
計算量は。
提出コード(PyPy3): Submission #2114223 - AtCoder Regular Contest 065
N, a, b = map(int, input().split()); a -= 1; b -= 1 P = [] # xでソートする点列 Q = [] # yでソートする点列 for i in range(N): x, y = map(int, input().split()) P.append((x-y, x+y, i)) Q.append((x+y, x-y, i)) # d(a, b)と見なす距離d d = max(abs(P[a][0] - P[b][0]), abs(P[a][1] - P[b][1])) # Union-Find *parent, = range(N) def root(x): if x == parent[x]: return x y = parent[x] = root(parent[x]) return y def unite(x, y): px = root(x); py = root(y) if px < py: parent[py] = px else: parent[px] = py C = [0]*N # C[i]: 点iから重複しないペア数 D = [0]*N # D[i]: 点iから2回重複するペア数 # 重複する点のペアか def check(P0, i0, j0): return abs(P0[i0][0] - P0[j0][0]) == abs(P0[i0][1] - P0[j0][1]) def solve(P0): P = P0[:] P.sort() # prev: どの点までuniteしたか s = t = 0; prev = -1 for i in range(N): x, y, i0 = P[i] while t < N and P[t][0] < x-d or (P[t][0] == x-d and P[t][1] <= y+d): t += 1 while s < N and (P[s][0] < x-d or (P[s][0] == x-d and P[s][1] < y-d)): s += 1 if s < t: # P_iと距離dの関係にある点列 P_s, P_{s+1}, ..., P_{t-1} j0 = P[s][2] unite(i0, j0) # P_i と P_s のunite if check(P0, i0, j0): D[i0] += 1 else: C[i0] += 1 if s < t-1: j0 = P[t-1][2] if check(P0, i0, j0): D[i0] += 1 C[i0] += t-s-2 else: C[i0] += t-s-1 # P_j と P_{j+1} のunite # 既にuniteし終わった点、もしくは点P_sからunite for j in range(max(prev, s), t-1): unite(P[j][2], P[j+1][2]) prev = t-1 solve(P) solve(Q) # S: 重複しないペア数, T: 2回重複するペア数 S = T = 0 r = root(a) for i in range(N): # 点a,bと同じグループに属するペア数を数える if root(i) == r: S += C[i]; T += D[i] print(S + T//2)