{ }codelper
尺取り法64ビット整数P0368

干し柿を干す日を選ぶ

n 日間の天気の記録がある。
i 日目に差した日ざしの量は s_i で、降った雨の量は w_i である。
干し柿は連続した何日かのあいだ干すが、そのあいだの日ざしの合計が S 以上で、雨の合計が R 以下でなければうまく乾かない。
干し始める日と干し終える日の選び方のうち、干し柿がうまく乾くものを「良い期間」と呼ぶ。
一日だけ干すのも期間である。
q 個の質問が与えられる。
質問 "S R" ごとに、良い期間の数、最も短い良い期間の日数、その期間が始まる日の番号を、空白一つで区切って一行に出力せよ。
最も短い良い期間が複数あれば、最も早く始まるものを選ぶ。
良い期間がなければ 0 0 0 を出力する。
例 1 の最初の質問 "5 3" では、良い期間は 4 日~5 日、5 日、4 日~6 日、5 日~6 日の四つで、最も短いのは 5 日の一日だけである。

入力
一行目に n と q が空白一つで区切って与えられる。
二行目に s_1, …, s_n が、三行目に w_1, …, w_n が空白一つで区切って与えられる。
続く q 行に S と R が空白一つで区切って与えられる。
1 ≤ n ≤ 9,000、1 ≤ q ≤ 150、0 ≤ s_i, w_i ≤ 10^9、0 ≤ S, R ≤ 10^13(すべて整数で、コンマなしの数字だけで与えられる)
出力
質問ごとに一行で、良い期間の数、最も短い良い期間の日数、その期間が始まる日の番号。
良い期間がなければ 0 0 0
例 1 · stdin
6 5
3 0 4 2 5 1
1 0 6 0 2 1
5 3
4 0
0 1
20 100
6 2
stdout
4 1 5
0 0 0
5 1 1
0 0 0
1 2 4
例 2 · stdin
3 2
1000000000 1000000000 1000000000
0 5 0
2500000000 5
3000000000 4
stdout
1 3 1
0 0 0
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

終わる日 r を一つに決めると、始まる日を前にずらすほど日ざしの合計も雨の合計も増えるだけです(値が 0 以上だからです)。
だから雨の条件は始まる日がある日 lo 以降のときだけ成り立ち、日ざしの条件はある日 hi 以前のときだけ成り立ちます。
r で終わる良い期間は、始まる日が lo から hi までの hi - lo + 1 個で、その中で最も短いものは hi から始まります。
r が一日増えると、lo も hi も後ろにしか動きません。
だから三つのポインタ(r、lo、hi)がそれぞれ最初から最後まで一度だけ通り、質問一つが n に比例する時間で終わります。
合計も足し直さず、lo..r の雨の合計と hi..r の日ざしの合計を一日ずつ入れたり抜いたりしながら持っていきます。
八つの言語の解き方は形がほとんど同じです。
C・C++・Java・C#・JavaScript・TypeScript は rainSum -= rain[lo++] のように引きながらポインタを動かすのを一行で書けますが、Python と Craykon は ++ を式の中で使えないので二行に分けて書きます。
罠その一:始まる日ごとに終わる日を一つずつ延ばしてすべての期間を見ると、質問一つで約 4,000 万個、全部で約 60 億個の期間を見ることになります。
雨が一度も降らず、途中でやめることもできない入力があります。
こう書いた Python の解答は時間内に終わらず、同じやり方の C の解答もこのコンピュータで最も大きい入力に 12 秒かかりました。
罠その二:lo が hi より後ろにあるなら、r で終わる良い期間はありません。
lo <= hi を確かめずに hi - lo + 1 を足すと負の数が足されます。
例 1 の最初の質問は 4 ではなく 2 になり、二番目の質問は 0 0 0 ではなく -1 1 3 になります。
罠その三:hi を動かす繰り返しに hi < r がないと、S が 0 のとき hi が r を越えて、日が一つもない期間を数えます。
例 1 の三番目の質問 "0 1" で JavaScript は 5 1 1 ではなく 12 -1 3 を出力します。
JavaScript はリストの終わりを越えて読んでも undefined を返すだけなので静かに間違え、Python は IndexError で、Java・C# は例外で止まります。
C・C++ は何が起こるか決まっていません。
罠その四:最も短い期間が複数あれば、最も早く始まるものを選ばなければなりません。
r を前から増やしていくので、< で比べれば先に見つけたものが残ります。
<= で比べると、例 1 の三番目の質問で始まる日が 1 ではなく 6 になります。
罠その五:合計は 9 × 10^12 まで大きくなります。
Java・C#・C・C++ は合計と S、R を long/long long に入れなければなりません。
Java で合計を int に入れると、例 2 の最初の質問で 30 億が -1294967296 に変わり、1 3 1 ではなく 0 0 0 が出ます。
Python の整数には大きさの制限がなく、JavaScript・TypeScript・Craykon の数は 2^53 まで正確なので、そのままで合います。
良い期間の数は多くても約 4,050 万なので int に入ります。

Pythonmain.py · 44 行
import sys  def solve(n, sun, rain, need, limit):    """햇볕 합 ≥ need, 비 합 ≤ limit인 구간의 수와 가장 짧은 구간(길이, 시작 날)."""    count = 0    best_len = best_start = 0    lo = 0          # 비 합이 limit 이하가 되는 가장 이른 시작 날    hi = 0          # 햇볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때)    rain_sum = 0    # lo..r의 비 합    sun_sum = 0     # hi..r의 햇볕 합    for r in range(n):        rain_sum += rain[r]        while rain_sum > limit:            rain_sum -= rain[lo]            lo += 1        sun_sum += sun[r]        while hi < r and sun_sum - sun[hi] >= need:            sun_sum -= sun[hi]            hi += 1        # 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다        if sun_sum >= need and lo <= hi:            count += hi - lo + 1            if best_len == 0 or r - hi + 1 < best_len:                best_len = r - hi + 1                best_start = hi + 1    return count, best_len, best_start  def main():    data = sys.stdin.buffer.read().split()    n, q = int(data[0]), int(data[1])    sun = [int(x) for x in data[2:2 + n]]    rain = [int(x) for x in data[2 + n:2 + 2 * n]]    out = []    pos = 2 + 2 * n    for _ in range(q):        need, limit = int(data[pos]), int(data[pos + 1])        pos += 2        out.append("%d %d %d" % solve(n, sun, rain, need, limit))    print("\n".join(out))  main()
JavaScriptmain.js · 37 行
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = data[0];const q = data[1];const sun = data.slice(2, 2 + n);const rain = data.slice(2 + n, 2 + 2 * n); // 햇볕 합 ≥ need, 비 합 ≤ limit인 구간의 수와 가장 짧은 구간(길이, 시작 날)function solve(need, limit) {  let count = 0;  let bestLen = 0;  let bestStart = 0;  let lo = 0; // 비 합이 limit 이하가 되는 가장 이른 시작 날  let hi = 0; // 햇볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때)  let rainSum = 0; // lo..r의 비 합  let sunSum = 0; // hi..r의 햇볕 합  for (let r = 0; r < n; r++) {    rainSum += rain[r];    while (rainSum > limit) rainSum -= rain[lo++];    sunSum += sun[r];    while (hi < r && sunSum - sun[hi] >= need) sunSum -= sun[hi++];    // 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다    if (sunSum >= need && lo <= hi) {      count += hi - lo + 1;      if (bestLen === 0 || r - hi + 1 < bestLen) {        bestLen = r - hi + 1;        bestStart = hi + 1;      }    }  }  return [count, bestLen, bestStart];} const out = [];for (let i = 0; i < q; i++) {  out.push(solve(data[2 + 2 * n + 2 * i], data[3 + 2 * n + 2 * i]).join(" "));}console.log(out.join("\n"));
TypeScriptmain.ts · 38 行
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = data[0];const q = data[1];const sun: number[] = data.slice(2, 2 + n);const rain: number[] = data.slice(2 + n, 2 + 2 * n); // 햇볕 합 ≥ need, 비 합 ≤ limit인 구간의 수와 가장 짧은 구간(길이, 시작 날)function solve(need: number, limit: number): [number, number, number] {  let count = 0;  let bestLen = 0;  let bestStart = 0;  let lo = 0; // 비 합이 limit 이하가 되는 가장 이른 시작 날  let hi = 0; // 햇볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때)  let rainSum = 0; // lo..r의 비 합  let sunSum = 0; // hi..r의 햇볕 합  for (let r = 0; r < n; r++) {    rainSum += rain[r];    while (rainSum > limit) rainSum -= rain[lo++];    sunSum += sun[r];    while (hi < r && sunSum - sun[hi] >= need) sunSum -= sun[hi++];    // 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다    if (sunSum >= need && lo <= hi) {      count += hi - lo + 1;      if (bestLen === 0 || r - hi + 1 < bestLen) {        bestLen = r - hi + 1;        bestStart = hi + 1;      }    }  }  return [count, bestLen, bestStart];} const out: string[] = [];for (let i = 0; i < q; i++) {  out.push(solve(data[2 + 2 * n + 2 * i], data[3 + 2 * n + 2 * i]).join(" "));}console.log(out.join("\n"));
JavaMain.java · 45 行
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int n = Integer.parseInt(st.nextToken());        int q = Integer.parseInt(st.nextToken());        long[] sun = new long[n];        long[] rain = new long[n];        st = new StringTokenizer(in.readLine());        for (int i = 0; i < n; i++) sun[i] = Long.parseLong(st.nextToken());        st = new StringTokenizer(in.readLine());        for (int i = 0; i < n; i++) rain[i] = Long.parseLong(st.nextToken());         StringBuilder out = new StringBuilder();        for (int k = 0; k < q; k++) {            st = new StringTokenizer(in.readLine());            long need = Long.parseLong(st.nextToken());            long limit = Long.parseLong(st.nextToken());            int count = 0, bestLen = 0, bestStart = 0;            int lo = 0;       // 비 합이 limit 이하가 되는 가장 이른 시작 날            int hi = 0;       // 햇볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때)            long rainSum = 0; // lo..r의 비 합            long sunSum = 0;  // hi..r의 햇볕 합            for (int r = 0; r < n; r++) {                rainSum += rain[r];                while (rainSum > limit) rainSum -= rain[lo++];                sunSum += sun[r];                while (hi < r && sunSum - sun[hi] >= need) sunSum -= sun[hi++];                // 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다                if (sunSum >= need && lo <= hi) {                    count += hi - lo + 1;                    if (bestLen == 0 || r - hi + 1 < bestLen) {                        bestLen = r - hi + 1;                        bestStart = hi + 1;                    }                }            }            out.append(count).append(' ').append(bestLen).append(' ').append(bestStart).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 44 行
using System;using System.Text; class Program{    static void Main()    {        string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(p[0]), q = int.Parse(p[1]);        long[] sun = Array.ConvertAll(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries), long.Parse);        long[] rain = Array.ConvertAll(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries), long.Parse);         var sb = new StringBuilder();        for (int k = 0; k < q; k++)        {            p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            long need = long.Parse(p[0]), limit = long.Parse(p[1]);            int count = 0, bestLen = 0, bestStart = 0;            int lo = 0;       // 비 합이 limit 이하가 되는 가장 이른 시작 날            int hi = 0;       // 햇볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때)            long rainSum = 0; // lo..r의 비 합            long sunSum = 0;  // hi..r의 햇볕 합            for (int r = 0; r < n; r++)            {                rainSum += rain[r];                while (rainSum > limit) rainSum -= rain[lo++];                sunSum += sun[r];                while (hi < r && sunSum - sun[hi] >= need) sunSum -= sun[hi++];                // 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다                if (sunSum >= need && lo <= hi)                {                    count += hi - lo + 1;                    if (bestLen == 0 || r - hi + 1 < bestLen)                    {                        bestLen = r - hi + 1;                        bestStart = hi + 1;                    }                }            }            sb.Append(count).Append(' ').Append(bestLen).Append(' ').Append(bestStart).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 39 行
#include <iostream>#include <vector>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int n, q;    cin >> n >> q;    vector<long long> sun(n), rain(n);    for (auto& v : sun) cin >> v;    for (auto& v : rain) cin >> v;     while (q--) {        long long need, limit;        cin >> need >> limit;        int count = 0, bestLen = 0, bestStart = 0;        int lo = 0;            // 비 합이 limit 이하가 되는 가장 이른 시작 날        int hi = 0;            // 햇볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때)        long long rainSum = 0; // lo..r의 비 합        long long sunSum = 0;  // hi..r의 햇볕 합        for (int r = 0; r < n; r++) {            rainSum += rain[r];            while (rainSum > limit) rainSum -= rain[lo++];            sunSum += sun[r];            while (hi < r && sunSum - sun[hi] >= need) sunSum -= sun[hi++];            // 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다            if (sunSum >= need && lo <= hi) {                count += hi - lo + 1;                if (bestLen == 0 || r - hi + 1 < bestLen) {                    bestLen = r - hi + 1;                    bestStart = hi + 1;                }            }        }        cout << count << ' ' << bestLen << ' ' << bestStart << '\n';    }    return 0;}
Cmain.c · 36 行
#include <stdio.h> static long long sun[9000], rain[9000]; int main(void) {    int n, q;    if (scanf("%d %d", &n, &q) != 2) return 0;    for (int i = 0; i < n; i++) if (scanf("%lld", &sun[i]) != 1) return 0;    for (int i = 0; i < n; i++) if (scanf("%lld", &rain[i]) != 1) return 0;     for (int k = 0; k < q; k++) {        long long need, limit;        if (scanf("%lld %lld", &need, &limit) != 2) return 0;        int count = 0, best_len = 0, best_start = 0;        int lo = 0;             /* 비 합이 limit 이하가 되는 가장 이른 시작 날 */        int hi = 0;             /* 햇볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때) */        long long rain_sum = 0; /* lo..r의 비 합 */        long long sun_sum = 0;  /* hi..r의 햇볕 합 */        for (int r = 0; r < n; r++) {            rain_sum += rain[r];            while (rain_sum > limit) rain_sum -= rain[lo++];            sun_sum += sun[r];            while (hi < r && sun_sum - sun[hi] >= need) sun_sum -= sun[hi++];            /* 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다 */            if (sun_sum >= need && lo <= hi) {                count += hi - lo + 1;                if (best_len == 0 || r - hi + 1 < best_len) {                    best_len = r - hi + 1;                    best_start = hi + 1;                }            }        }        printf("%d %d %d\n", count, best_len, best_start);    }    return 0;}
Craykonmain.cray · 45 行
첫줄 = コンソール.数読み();n = 첫줄[0];q = 첫줄[1];볕 = コンソール.数読み();비 = コンソール.数読み(); 繰り返す (k = 0; k < q; k++){    줄 = コンソール.数読み();    need = 줄[0];    limit = 줄[1];    센수 = 0;    짧은길이 = 0;    짧은시작 = 0;    lo = 0; // 비 합이 limit 이하가 되는 가장 이른 시작 날    hi = 0; // 볕 합이 need 이상인 가장 늦은 시작 날(되는 날이 있을 때)    비합 = 0; // lo..r의 비 합    볕합 = 0; // hi..r의 볕 합    繰り返す (r = 0; r < n; r++)    {        비합 = 비합 + 비[r];        間 (비합 > limit)        {            비합 = 비합 - 비[lo];            lo = lo + 1;        }        볕합 = 볕합 + 볕[r];        間 (hi < r && 볕합 - 볕[hi] >= need)        {            볕합 = 볕합 - 볕[hi];            hi = hi + 1;        }        // 시작 날이 lo 이상 hi 이하이면 두 조건이 모두 맞는다        もし (볕합 >= need && lo <= hi)        {            센수 = 센수 + hi - lo + 1;            もし (짧은길이 == 0 || r - hi + 1 < 짧은길이)            {                짧은길이 = r - hi + 1;                짧은시작 = hi + 1;            }        }    }    コンソール.書く(센수 + " " + 짧은길이 + " " + 짧은시작);}