AGC011 B Colorful Creatures
AtCoder Grand Contest 011の B の Colorful Creatures を解いた。
解説と違ってたから、解説を書く。
2分探索を使って解いた。
問題はこれ
B - Colorful Creatures。
解説はこれ
https://img.atcoder.jp/agc011/editorial.pdf。
解説
全ての生き物の色が異なるので、大きさでソートすることで、
としても一般性を失わない。
最終的に、 番目の生き物の色になるには、
を小さい方から順番に吸収していくことができればよい。
つまり、次のような条件を考えれば良い。
を次のように定める。
、
。
が上のように定まるのは、自分より小さい生き物は必ず吸収できるためです。
このとき、任意の に対して、
を満たせば良い。これが条件。
これを全ての に関してやればよいが、それでは になってしまう。
ところで、この条件はある に関して成立すれば、それ以降の添字でも成立する。
これは、実際は が に依存しない数列だからです。
というわけで、2分探索を実行できます。
解答
解答では、 が に依存しないことを利用して少し短くしています。
別に、毎回計算しても間に合います。
というか、初めはこれで通しました。
コメントアウトに書いておきます。
from itertools import accumulate N = int( input()) A = list( map( int, input().split())) A.sort() l = -1 r = N-1 B = list( accumulate(A)) while r - l > 1: m = (l+r)//2 check = 1 #now = sum(A[:m+1]) for i in range(m+1,N): if A[i] <= B[i-1]*2: pass # if A[i] <= now*2: # now += A[i] else: check = 0 break if check == 1: r = m else: l = m print(N-r)
おまけ
100msくらい変わる。
ちなみに、解説は だけど、これは です。
ARC083/ABC074 D Restoring Road Network
AtCoder Begginer Contest 083、AtCoder Regular Contest 074 D Restoring Road Network
よくわからなかったので、解説の解説をする。
他の人のコードを見ながら勉強した。
問題がこれ
D - Restoring Road Network。
解説がこれ
https://img.atcoder.jp/arc083/editorial.pdf。
解説
各 に対して、 の最小値の長さの辺は必ず存在する。
これで十分では?と思ったけど、そうじゃなさげ。
でも、その時点で最小の重みを持つ辺は確定しそうということが分かる。
それをやってみる。
を のリストで、対角成分が でそれ以外の全ての成分が とする。
ワーシャルフロイドをする。
を最小値から順番にとっていく。
その辺の重みを とし、辺を とする。
まず、それを加えるかどうか考えよう。
でグラフの距離を表すことにする。
全ての 以外の頂点 に対して、次の3パターンが考えられる。
1. 任意の に対して、 :
こういう辺を追加してもよい。
これが最適である。
まず、これより大きい辺との大小比較をする必要はない。
また、この辺の追加により、最短経路が更新される可能性はない。
このとき、 と更新する。
2. ある に対して、 :
同じ距離だから、こういう辺を追加する必要はない。
3. ある に対して、 :
このような、グラフは存在しない。
解答
import heapq N = int( input()) A = [ list( map( int, input().split())) for _ in range(N)] Q = [] for i in range(N-1): for j in range(i+1,N): heapq.heappush(Q, [A[i][j],i,j]) WF = [ [10**9+1]*N for _ in range(N)] for i in range(N): WF[i][i] = 0 cnt = 0 while Q: a, s, g = heapq.heappop(Q) addcheck = 1 for i in range(N): #新しく辺(s,g)を追加できるかどうかを判断する。 #小さい辺から順番にチェックしているので、より大きい辺との大小比較の必要がない。 #もう一つ、最短経路が更新される可能性があるが、それは今の辺より真に大きい辺のみなので、問題ない。 if i == s or i == g: continue else: if WF[s][i] + WF[i][g] == a: addcheck = 0 elif WF[s][i] + WF[i][g] < a: break else: WF[s][g] = a WF[g][s] = a if addcheck: cnt += a continue print(-1) break else: print( cnt)
Speed keys (org-mode)
Dwango Programming Contest V / 第5回 ドワンゴからの挑戦状 予選 B - Sum AND Subarrays
Dwango Programming Contest V / 第5回 ドワンゴからの挑戦状 予選 B - Sum AND Subarrays
B - Sum AND Subarrays
を解いた。
公式解説はこれ
https://img.atcoder.jp/dwacon5th-prelims/editorial.pdf。
bit演算系の問題は苦手だけど、初めてコンテスト中に解けた。
理解の整理のために解説を書くことにした。
解説
bit演算といえば、2進数表示の各桁ごとに見るのがよくある。
一般に、ピッタリの値を求めるよりも以上の値を確認するほうが"楽"らしい。
これを前提に考えると、 を考えるのが良さげに見えてくる。
特に、大きい桁から決めていけばいいことが分かる。
というのも、
なので、上の桁を確定した後に、それ以下の桁で数の大きさが逆転されることがない。
例えば、
10000 01111
では、10000 の方が大きい。
というわけで、上の桁から決めていこう。
と なので、部分列の和は高々 である。
ここで、
なので、 くらいまで考えれば良い。
次に、 桁目を確定する方法を解説する。
非負整数 が与えられたとき、 の 2進数表示において、 が であることは、
と同値です。
というわけで、 個以上の部分列に対して、上記の条件が満たされることを確かめれば良い。
あとは、プログラムの中に説明をかく。
from itertools import accumulate N, K = map( int, input().split()) A = list( map( int, input().split())) accA = [0] + list( accumulate(A)) V = [1]*(N*(N+1)//2) ans = 0 check = False for i in range(40,-1,-1): W = [0]*(N*(N+1)//2) for l in range(N): for r in range(l+1,N+1): if V[l*(2*N+1-l)//2+r-l-1] == 0:#Vは現時点で採用されている部分列を示している #l*(2*N+1-l)//2+r-l-1はVの大きさに合わせて何番目を更新するか決定した。(時間節約のため) continue if (accA[r]-accA[l])%(2**(i+1)) >= 2**i: W[l*(2*N+1-l)//2+r-l-1] = 1 if check == False:#初めに並ぶ 0 を無視するため if sum(W) >= K: check == True else:#無視して次のloopに遷移する continue if sum( V and W) >= K:#すでに確定している部分列と合わせて、K個以上あれば2**i桁目を採用する #V and W はlistの各成分のbool値の論理積を取っている ans += 2**i V = V and W print(ans)
2015 IndeedなうB D Game on a Grid
これ
D: Game on a Grid - Indeedなう(オープンコンテストB) | AtCoder
です。
最小全域木の問題です。
Prim法で解きました。
Kruskal法の、やり方がわからなかった。
解説曰く、できるらしい。
Prim法の解説記事です。
最小全域木をpythonで - kamojirobrothersのブログ。
行列の向きが AtCoder のいつものやつと違って、混乱した。
プログラムはこれ。
from heapq import * def find(A,x): p = A[x] if p == x: return x a = find(A,p) A[x] = a return a def union(A, x, y): if find(A,x) > find(A,y): bx, by = find(A,y), find(A,x) else: bx, by = find(A,x), find(A,y) A[y] = bx A[by] = bx H, W = map( int, input().split()) sx, sy = map( int, input().split()) gx, gy = map( int, input().split()) P = [ list( map( int, input().split())) for _ in range(H)] V = [ i for i in range(H*W)] sx, sy = sx-1, sy-1 gx, gy = gx-1, gy-1 ans = P[sy][sx] K = [] heapify(K) if sx != max(sx-1,0): heappush(K, ( -P[sy][sx-1]*P[sy][sx] , sx-1, sy)) if sx != min(sx+1,W-1): heappush(K, ( -P[sy][sx+1]*P[sy][sx] , sx+1, sy)) if sy != max(sy-1, 0): heappush(K, ( -P[sy-1][sx]*P[sy][sx], sx, sy-1)) if sy != min(sy+1,H-1): heappush(K, ( -P[sy+1][sx]*P[sy][sx], sx, sy+1)) s = sx*H+sy while K: p, x, y = heappop(K) if find(V, sx*H + sy) != find(V, x*H+y): union(V, sx*H + sy, x*H+y) ans += P[y][x] ans += -p if x != max(x-1,0): if find(V, (x-1)*H+y) != find(V, s): heappush(K, ( -P[y][x-1]*P[y][x] , x-1, y)) if x != min(x+1,W-1): if find(V, (x+1)*H + y) != find(V,s): heappush(K, ( -P[y][x+1]*P[y][x] , x+1, y)) if y != max(y-1, 0): if find(V, x*H+y-1) != find( V, s): heappush(K, ( -P[y-1][x]*P[y][x], x, y-1)) if y != min(y+1,H-1): if find(V, x*H+y+1) != find(V,s): heappush(K, ( -P[y+1][x]*P[y][x], x, y+1)) print(ans)