AtCoder Beginner Contest 126

ルール変更でBeginnerになりました。

atcoder.jp

Nの制約がテストケースと違う気がする。 あまり確率の問題は得意ではないが、これはそこまで難しくない。

まずサイコロで初期確率は1 / Nずつになる。 さらにそれぞれの場所から始めたとき、Kを超えるまで連続してコインで面を出し続けなければならない。

何回出し続ける必要があるか、はlog Kなので10000だと14程度なはずなのにこれが通らず、30ぐらいにしたら通った。

def solve(N, K):
    b = 1 / N
    poss = [b for _ in range(N + 1)]
    for n in range(30):
        for i in range(N + 1):
            if i == 0:
                continue
            if (i << n) < K <= (i << n + 1):
                poss[i] = b * (0.5 ** (n + 1))

    return "{:1.12f}".format(sum(poss[1:]))


assert (solve(3, 10) == "0.145833333333")
assert (solve(1, 1) == "1.000000000000")
assert (solve(1, 2) == "0.500000000000")
assert (solve(2, 2) == "0.750000000000")
assert (solve(2, 2) == "0.750000000000")
assert (solve(1, 3) == "0.250000000000")
assert (solve(1, 4) == "0.250000000000")
assert (solve(10000, 10000) == "0.333431365967")
assert (solve(100000, 5) == "0.999973749998")
assert (solve(1, 10000) == "0.000061035156")

if __name__ == "__main__":
    N, K = tuple(map(int, input().split(" ")))
    print(solve(N, K))

最近、問題文の配列が1から始まるとき、配列は0番目を余分に取るようにしてる。こっちのほうがミスが少ない気がするのと、 結局heapの配列上の実装などを考えるとき1から始まることを意識しないとダメなので。

atcoder.jp

これは例示が若干ひっかけ。 同じ色に塗られていれば偶数であるが、偶数であれば同じ色、というわけではない。

何の疑いもなく、単に幅優先探索をすればよい。 colorの決定をvisited代わりにすると実装が楽。

木だから深さ優先探索もいけるかな。

from collections import deque


def solve(N, UVWs):
    vertexes = {}

    for u, v, w in UVWs:
        vertexes.setdefault(u, []).append((v, w % 2))
        vertexes.setdefault(v, []).append((u, w % 2))

    colors = [-1 for _ in range(N + 1)]
    colors[1] = 0
    que = deque()
    que.append((1, colors[1]))
    while que:
        u, c = que.popleft()
        for v, w in vertexes[u]:
            if colors[v] == -1:
                colors[v] = (c + w) % 2
                que.append((v, colors[v]))
    return "\n".join(str(c) for c in colors[1:])


assert (solve(3, [(1, 2, 2), (2, 3, 1)]) == "0\n0\n1")
assert (solve(5, [(2, 5, 2), (2, 3, 10), (1, 3, 8), (3, 4, 2)]) == "0\n0\n0\n0\n0")

if __name__ == "__main__":
    N = int(input())
    UVWs = [tuple(map(int, input().split(" "))) for _ in range(N - 1)]
    print(solve(N, UVWs))

atcoder.jp

これは一瞬。

魔法を使えばある一つのカードの整数が判明する。これによってこの一つと偶数条件で対になっているカードが判明し、連鎖的に対になっているカードがすべて判明する。

つまり1回の魔法により、一つの集合がすべてわかることになるので、UnionFindを使って、集合の数を取ればよい。

珍しく、assert文を一つも書かなかった。

class UnionFind:
    import sys
    sys.setrecursionlimit(100000)

    def __init__(self, n):
        self.parents = [i for i in range(n + 1)]

    def root(self, i):
        if self.parents[i] == i:
            return i
        else:
            self.parents[i] = self.root(self.parents[i])
            return self.parents[i]

    def unite(self, i, j):
        self.parents[self.root(self.parents[i])] = self.root(j)

    def is_unite(self, i, j):
        return self.root(i) == self.root(j)


def solve(N, XYZs):
    uf = UnionFind(N)
    for x, y, z in XYZs:
        uf.unite(x, y)
    return len(set([uf.root(i) for i in range(1, N + 1)]))


if __name__ == "__main__":
    N, M = tuple(map(int, input().split(" ")))
    XYZs = [tuple(map(int, input().split(" "))) for _ in range(M)]
    print(solve(N, XYZs))

Fはジャッジ中なのでまた今度。