Official
C - お菓子の詰め合わせ / Assortment of Sweets Editorial
by
C - お菓子の詰め合わせ / Assortment of Sweets Editorial
by
harurun4635
尺取法の基本的な問題です。まずは、\(C_{\min}\) を求めましょう。
各 \(r\) を固定したとき、「ある \(l=i\) 以降ではすべて条件を満たし、\(l=i-1\) 以前ではすべて条件を満たさない」ような \(i\) がただ1つ存在するはずです。このような \(i\ (=i_r)\) が各 \(r\) について高速に求められればよいです。また、\(r_1\leq r_2\) であれば \(i_{r_1}\leq i_{r_2}\) となるはずです。
ということで、以下のようなアルゴリズムが正当です。
変数 \(S=0\) と \(l=0\) を用意する。
\(r=0,1,\dots,N-1\) の順に、以下を繰り返す。
\(S\mathrel{+}=W_r\) と更新する。
\(S>C_{\min}\) である間、以下を繰り返す。
- \(S\mathrel{-}=W_l,\ l\mathrel{+}=1\) と更新する。
このとき、左端が \(l,l+1,\dots,r\) の区間はすべて条件を満たすので、\(r-l+1\) を答えに加算する。
以上は尺取法などと呼ばれるアルゴリズムです。(ほとんど同様なコードを \(l\) で for 文を回しても解くことができますが、非自明な区間を扱うことになりがちなので注意してください)
実装例
n, k = map(int, input().split())
w = list(map(int, input().split()))
c = min(map(int, input().split()))
l = s = ans = 0
for r in range(n):
s += w[r]
while s > c:
s -= w[l]
l += 1
ans += r - l + 1
print(ans)
n, k = map(int, input().split())
w = list(map(int, input().split()))
c = min(map(int, input().split()))
s = r = 0
ans = 0
for l in range(n):
while r < n and s + w[r] <= c:
s += w[r]
r += 1
ans += r - l
s -= w[l] # この瞬間 s が負なことがある
print(ans)
posted:
last update:
