누적 합 (Prefix Sum) 은 배열 a[0..N-1] 에 대해 S[i] = a[0] + a[1] + ... + a[i-1] (또는 1-indexed S[i] = a[1] + ... + a[i]) 을 미리 계산해 두고, 임의 구간 합 a[l..r] 을 O(1) 에 구하는 정형.
sum(l, r) = S[r + 1] - S[l]
문제 풀이에서 “구간 N 개 × Q 개 쿼리” 가 O(N + Q) 로 떨어지는 가장 기본 패턴.
문제 상황과 동기
Σ a[l..r] 을 Q 번 묻는다.
naive: 매 쿼리마다 l..r 순회. O(N · Q). N=Q=10^5 면 10^10, 절대 안 됨.
prefix sum: O(N) 전처리 + O(1) 쿼리. 총 O(N + Q).
핵심 통찰: 합 함수 S(x) 를 한 번만 계산하면, 임의 구간은 두 점의 차. 누적은 도함수 / 미분이 가능한 모든 연산에 일반화 (XOR, AND/OR, min/max 같은 단조 연산은 누적 가능 / 불가능).
// 1D prefix sum, O(N) 전처리 + O(1) 쿼리#include <bits/stdc++.h>using namespace std;int main() { int n, q; cin >> n >> q; vector<long long> a(n), S(n + 1, 0); for (auto& v : a) cin >> v; for (int i = 1; i <= n; i++) S[i] = S[i-1] + a[i-1]; while (q--) { int l, r; cin >> l >> r; // 1-indexed [l, r] cout << S[r] - S[l-1] << "\n"; }}
# itertools.accumulate 로 O(N) 한 줄 prefix sumfrom itertools import accumulateimport sysinput = sys.stdin.readlinen, q = map(int, input().split())a = list(map(int, input().split()))S = [0] + list(accumulate(a))out = []for _ in range(q): l, r = map(int, input().split()) # 1-indexed [l, r] out.append(str(S[r] - S[l-1]))print("\n".join(out))
// long 으로 합. 큰 N 의 합 오버플로우 방지import java.util.*;import java.io.*;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()); int q = Integer.parseInt(st.nextToken()); long[] S = new long[n + 1]; st = new StringTokenizer(br.readLine()); for (int i = 1; i <= n; i++) S[i] = S[i-1] + Long.parseLong(st.nextToken()); StringBuilder sb = new StringBuilder(); while (q-- > 0) { st = new StringTokenizer(br.readLine()); int l = Integer.parseInt(st.nextToken()); int r = Integer.parseInt(st.nextToken()); sb.append(S[r] - S[l-1]).append('\n'); } System.out.print(sb); }}
💬 댓글