Official

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: