{ }codelper
累積和64ビット整数P0039

区間の和をすばやく

n 個の整数からなる数列 a_1, a_2, …, a_n と q 個の質問が与えられる。
各質問は二つの数 l, r からなり、a_l から a_r まで(両端を含む)の和を問う。
質問ごとに答えを一行ずつ、与えられた順に出力せよ。
一行目に n と q、二行目に a_1 … a_n、続く q 行に l と r が空白区切りで与えられる。

入力
1 ≤ n ≤ 10,000、1 ≤ q ≤ 20,000、-10^9 ≤ a_i ≤ 10^9、1 ≤ l ≤ r ≤ n
出力
q 行。
各行にその質問の区間の和
例 1 · stdin
5 3
3 -1 4 1 -5
1 3
2 5
4 4
stdout
6
-1
1
main.py
入力(stdin)

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

$ 準備完了

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

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

質問ごとに l から r まで足し直すと最大 10^4 × 2×10^4 = 2×10^8 回の足し算になり、Python では時間内に終わりません(C・C++ はまだ耐えますが、n, q がもっと大きくなれば同じです)。
Craykon は実行ステップが約 5 千万回を超えると「時間がかかりすぎ」で止まるので、この方法は機械の速さに関係なく必ず時間切れです。
累積和 P[i] = a_1 + … + a_i(P[0] = 0)を一度作っておけば、答えは P[r] - P[l-1] ですぐ出ます。
よくある間違いは P[r] - P[l] と引いて a_l を落とすことと、和が最大 10^13 になるのに C・C++・Java・C# で int(約 21 億)に入れることです — long/long long を使います。
Python は整数に上限がなく、JavaScript・TypeScript・Craykon は 2^53 まで正確なので大丈夫です。
入力が多いので Python は sys.stdin、Java は BufferedReader、C++ は sync_with_stdio(false) で速く読みます。
Craykon のリストは 10,000 個までなので、n + 1 マスの P の代わりに a[i] = a[i] + a[i - 1] で a をその場で累積和に変え、質問は コンソール.数読み() で一行ずつ読みます。

Pythonmain.py · 14 行
import sys data = sys.stdin.buffer.read().split()n, q = int(data[0]), int(data[1])prefix = [0] * (n + 1)  # prefix[i] = 앞에서 i개의 합for i in range(n):    prefix[i + 1] = prefix[i] + int(data[2 + i])out = []pos = 2 + nfor _ in range(q):    l, r = int(data[pos]), int(data[pos + 1])    pos += 2    out.append(prefix[r] - prefix[l - 1])print("\n".join(map(str, out)))
JavaScriptmain.js · 10 行
const t = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, q] = t;const prefix = new Array(n + 1).fill(0); // prefix[i] = 앞에서 i개의 합for (let i = 0; i < n; i++) prefix[i + 1] = prefix[i] + t[2 + i];const out = [];for (let k = 0, pos = 2 + n; k < q; k++, pos += 2) {  const l = t[pos], r = t[pos + 1];  out.push(prefix[r] - prefix[l - 1]);}console.log(out.join("\n"));
TypeScriptmain.ts · 11 行
import { readFileSync } from "node:fs";const t: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, q] = t;const prefix: number[] = new Array(n + 1).fill(0); // prefix[i] = 앞에서 i개의 합for (let i = 0; i < n; i++) prefix[i + 1] = prefix[i] + t[2 + i];const out: number[] = [];for (let k = 0, pos = 2 + n; k < q; k++, pos += 2) {  const l = t[pos], r = t[pos + 1];  out.push(prefix[r] - prefix[l - 1]);}console.log(out.join("\n"));
JavaMain.java · 22 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(br.readLine());        int n = Integer.parseInt(st.nextToken()), q = Integer.parseInt(st.nextToken());        long[] prefix = new long[n + 1]; // prefix[i] = 앞에서 i개의 합        st = new StringTokenizer(br.readLine());        for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + Long.parseLong(st.nextToken());        StringBuilder out = new StringBuilder();        for (int k = 0; k < q; k++) {            st = new StringTokenizer(br.readLine());            int l = Integer.parseInt(st.nextToken()), r = Integer.parseInt(st.nextToken());            out.append(prefix[r] - prefix[l - 1]).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 21 行
using System;using System.IO;using System.Text; class Program{    static void Main()    {        string[] t = Console.In.ReadToEnd().Split((char[])null, StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(t[0]), q = int.Parse(t[1]);        long[] prefix = new long[n + 1]; // prefix[i] = 앞에서 i개의 합        for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + long.Parse(t[2 + i]);        var sb = new StringBuilder();        for (int k = 0, pos = 2 + n; k < q; k++, pos += 2)        {            int l = int.Parse(t[pos]), r = int.Parse(t[pos + 1]);            sb.Append(prefix[r] - prefix[l - 1]).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 20 行
#include <iostream>#include <vector> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n, q;    std::cin >> n >> q;    std::vector<long long> prefix(n + 1, 0); // prefix[i] = 앞에서 i개의 합    for (int i = 0; i < n; i++) {        long long x;        std::cin >> x;        prefix[i + 1] = prefix[i] + x;    }    for (int k = 0; k < q; k++) {        int l, r;        std::cin >> l >> r;        std::cout << prefix[r] - prefix[l - 1] << '\n';    }}
Cmain.c · 21 行
#include <stdio.h>#include <stdlib.h> int main(void) {    int n, q;    scanf("%d %d", &n, &q);    long long *prefix = malloc((n + 1) * sizeof *prefix); // prefix[i] = 앞에서 i개의 합    prefix[0] = 0;    for (int i = 0; i < n; i++) {        long long x;        scanf("%lld", &x);        prefix[i + 1] = prefix[i] + x;    }    for (int k = 0; k < q; k++) {        int l, r;        scanf("%d %d", &l, &r);        printf("%lld\n", prefix[r] - prefix[l - 1]);    }    free(prefix);    return 0;}
Craykonmain.cray · 23 行
첫줄 = コンソール.数読み();n = 첫줄[0];q = 첫줄[1];// a를 그 자리에서 누적 합으로 바꾼다: a[i] = 앞에서 i+1개의 합a = コンソール.数読み();繰り返す (i = 1; i < n; i++){    a[i] = a[i] + a[i - 1];}繰り返す (k = 0; k < q; k++){    lr = コンソール.数読み();    l = lr[0];    r = lr[1];    もし (l == 1)    {        コンソール.書く(a[r - 1]);    }    でなければ    {        コンソール.書く(a[r - 1] - a[l - 2]);    }}