[ABC258E] Packing Potatoes を Pythonで解く
考え方
「尺取り法」と「周期性の処理」の合わせ技です。アルゴリズムは決して高度ではありませんが、実装に苦戦してしたので、備忘録として残しておきます。
例として、
このジャガイモ重量周期の始まりと終わりを辺で結んだ、グラフに置き換えて考えるのがポイントです。それぞれ出次数が
実装メモ
実装で苦戦したため、変数名はあえて長めにして意味を取りやすくしています。まず、標準入力の処理、今回はクエリはいったん記録します。
N,Q,X = map(int,input().split())
W = list(map(int,input().split()))
query = []
for _ in range(Q):
query.append(int(input()))
ジャガイモの一周期分の重量総和を求めます。目標重量
空箱にあらかじめ
SumWgt = sum(W)
Ncnt = 0
while X > SumWgt:
Ncnt += N * (X//SumWgt)
X %= SumWgt
尺取り法の部分は、
Arrow = [-1] * N
PotatoCnt = [0] * N
s,r = 0,0
for l in range(N):
if l > 0:
s -= W[l-1]
while s < X:
s += W[r%N]
r += 1
Arrow[l] = r%N
PotatoCnt[l] = r-l
グラフ頂点の周期性の処理を行います。周期の大きさ:Cycle、周期性に入る前の頂点数:PreCyeを計算します。これらを計算しておけば、
既視のデータは、Seenにindexを入れておきます。見たことのあるデータを見つけた場合には、保存されているindexが、PreCycに相当します。通ってきたグラフの頂点の順序をRecに記録していきます。Rec[0]に記録されているのは、
x = 0
idx = 1
Seen = [-1] * N
Rec = []
while idx :
if Seen[x] < 0:
Seen[x] = idx
Rec.append(x)
idx += 1
else:
PreCyc = Seen[x]
Cycle = idx - PreCyc
break
x = Arrow[x]
クエリで与えられた
ans = []
for k in query:
if k - PreCyc > 0:
k = (k - PreCyc) % Cycle + PreCyc
a = PotatoCnt[Rec[k-1]] + Ncnt
ans.append(a)
print(*ans, sep="\n")
結構大変でした。周期性の処理は苦手です。
Discussion