🔖

AtCoder ARC121 個人的メモ

2021/05/30に公開

所感

a1完
終了6分でbをacした
悔しい
初のレート4桁到達
嬉しい
arc121score

A - 2nd Greatest Distance

全部の家同士の距離を求めて、それをソートするのはO(N^2logN)掛かるので無理。
最も遠い家の組み合わせは
簡単のため、家同士の距離がxの値のみで定まるとする。
昇順(小さい順)でソート済みの家の座標xをx_1,x_2\cdots \x_{N-1},x_Nとする。
このとき最も遠い家同士の距離は|x_1-x_N|となる。
二番目に遠い家同士の距離は以下の3つの組み合わせのどれか

  • |x_1-x_{N-1}|
  • |x_2-x_N|
  • |x_2-x_{N-1}|

よって、この3つの内最大の値が2番目に遠い家同士の距離。

今回はxだけではなくyもあるので、2番目に遠い家同士の距離の候補は6つになる。
なのでその候補の内、距離が最大になる家の組とは違う組み合わせの家かつ2番目に距離が最大になる家の組み合わせを探す。

N = int(input())
X, Y = [], []
for i in range(N):
    x, y = map(int, input().split())
    X.append((x, i))
    Y.append((y, i))
X.sort()
Y.sort()

candidate = []
for xa, ia in X[-2:]:
    for xb, ib in X[:2]:
        candidate.append([xa - xb, {ia, ib}])

for ya, ia in Y[-2:]:
    for yb, ib in Y[:2]:
        candidate.append([ya - yb, {ia, ib}])

candidate.sort(reverse=True)
_, max_pair = candidate[0]
for distance, pair in candidate[1:]:
    if max_pair != pair:
        print(distance)
        break

B - RGB Matching

ペアを作る際、同じ色の2匹を選べるなら選んだほうが良さそう。
犬の総計は偶数なので、3色の数は(偶数、偶数、偶数)か(奇数、奇数、偶数)のいずれかと分かる。
前者の場合は、全ての色において同じ色どうしでペアを作れるので、答えは0でおk。
後者の場合は以下の2つの内のどっちかが最適。

  • 奇数の色どうしから1匹ずつ選んで1つの色が異なるペアを作る。
  • 奇数の色2色から1匹ずつと偶数の色1色から2匹選んで、2つの色が異なるペアを作る。

これを実装すればおk。

しかし、最適なペアを探す際に全探索するとTLEしそうなので高速化する。
最適なペアはできる限り美しさの差が小さいペアとなるので、二分探索で高速化できる。

acしたときのコードがとても汚かったので、かなり修正したコードを載せておく。

from bisect import bisect_left


def find_closest_val(lst: list, val: int):
    """
    数列lst内で数値valに最も近い値を二分探索で探し、valとの絶対値の差を返す
    """
    # leftはlst内のval未満でvalに最も近い要素のインデックス
    left = bisect_left(lst, val)
    res = abs(val - lst[left - 1])
    if left < len(lst):
        res = min(res, abs(val - lst[left]))
    return res


N = int(input())
lst = [[] for _ in range(3)]
color = "RGB"
for _ in range(N * 2):
    a, c = input().split()
    lst[color.index(c)].append(int(a))
A, B, C = lst

while len(C) % 2 == 1:  # Cが偶数個の色になるようにA,B,Cの名前を変える
    C, A, B = A, B, C

if len(A) % 2 == 0:     # (A,B,C)=(偶,偶,偶)の場合は答え0
    print(0)
    exit()

# 以降(A,B,C)=(奇,奇,偶)

A.sort()  # 二分探索のためのソート
B.sort()

# A,Bから1つずつ選ぶ場合
ans = min(find_closest_val(B, a) for a in A)

# A,CとB,Cで1つずつ選ぶ場合
if C:
    res = 0
    res += min(find_closest_val(A, c) for c in C)
    res += min(find_closest_val(B, c) for c in C)
    ans = min(ans, res)

print(ans)

修正前のコード
N = int(input())
INF = 10 ** 18
red = []
green = []
blue = []
for _ in range(N * 2):
    a, i = input().split()
    a = int(a)
    if i == "R":
        red.append(a)
    elif i == "G":
        green.append(a)
    else:
        blue.append(a)

if all(len(lst) % 2 == 0 for lst in (red, green, blue)):
    print(0)
    exit()
elif len(red) % 2 == 0:
    C = red
    A = green
    B = blue
elif len(green) % 2 == 0:
    C = green
    A = red
    B = blue
else:
    C = blue
    A = red
    B = green

if len(A) > len(B):
    A, B = B, A
A.sort()
B.sort()

# xyの組み合わせ
ans = INF
for a in A:
    left = 0
    right = len(B)
    while right - left > 1:
        mid = left + right
        mid //= 2
        if B[mid] < a:
            left = mid
        else:
            right = mid

    ans = min(ans, abs(a - B[left]))
    if left + 1 < len(B):
        ans = min(ans, abs(a - B[left + 1]))

# zx,zy
res = 0
pick_i = -1
for lst in (A, B):
    tmp = INF
    for i in range(len(C)):
        left = 0
        right = len(lst)
        while right - left > 1:
            mid = left + right
            mid //= 2
            if lst[mid] < C[i]:
                left = mid
            else:
                right = mid

        cal = abs(C[i] - lst[left])
        if tmp > cal:
            tmp = cal
            pick_i = i
        if left + 1 < len(lst):
            cal = abs(C[i] - lst[left + 1])
            if tmp > cal:
                tmp = cal
                pick_i = i
    if pick_i != -1:
        C[-1], C[pick_i] = C[pick_i], C[-1]
        C.pop()
    res += tmp

ans = min(ans, res)
print(ans)

参考

二分探索の実装
https://docs.python.org/ja/3/library/bisect.html#searching-sorted-lists
コードの修正
https://atcoder.jp/contests/arc121/submissions/22777524

Discussion