2D 배열 C[i][j]에 파스칼 삼각형 저장. 이전 행에서 두 값을 더하는 O(N^2) 방법.
단일 쿼리라면 1D 배열로 공간 최적화:
배열을 오른쪽에서 왼쪽으로 갱신 → in-place 업데이트
MOD 처리
대부분 문제는 답을 109+7 로 나눈 나머지 요구.
DP 점화식은 덧셈만 사용하므로 MOD 연산 자연스럽게 적용:
C[i][j]=(C[i−1][j−1]+C[i−1][j])modM
Lucas’ Theorem (대형 n)
n 이 매우 크고 소수 p 로 나눈 나머지를 구할 때:
(n{)}{k}≡{∏i}(n{)i}{ki}(mod{)p}
여기서 n=∑nipi, k=∑kipi 는 p 진법 표현.
알고리즘
알고리즘 1: 2D 파스칼 삼각형 (O(N^2) 공간/시간)
// C[i][j] = C(i, j) mod MODconst int MOD = 1e9 + 7;vector<vector<long long>> C(n + 1, vector<long long>(n + 1, 0));for (int i = 0; i <= n; i++) { C[i][0] = 1; for (int j = 1; j <= i; j++) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % MOD;}// C[n][k] = C(n, k) mod MOD
알고리즘 2: 1D 공간 최적화 (O(N) 공간)
vector<long long> C(n + 1, 0);C[0] = 1;for (int i = 1; i <= n; i++) for (int j = i; j >= 1; j--) // 오른쪽에서 왼쪽으로 C[j] = (C[j] + C[j-1]) % MOD;// C[k] = C(n, k) mod MOD
알고리즘 3: 팩토리얼 + 역원 (단일 쿼리 O(N) 전처리)
n 이 크고 다중 쿼리일 때: 팩토리얼과 모듈러 역원을 전처리.
const int MAXN = 2e5 + 5;const int MOD = 1e9 + 7;long long fact[MAXN], inv_fact[MAXN];long long power(long long a, long long b, long long mod) { long long res = 1; a %= mod; for (; b > 0; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res;}void precompute(int n) { fact[0] = 1; for (int i = 1; i <= n; i++) fact[i] = fact[i-1] * i % MOD; inv_fact[n] = power(fact[n], MOD - 2, MOD); for (int i = n - 1; i >= 0; i--) inv_fact[i] = inv_fact[i+1] * (i+1) % MOD;}long long C(int n, int k) { if (k < 0 || k > n) return 0; return fact[n] % MOD * inv_fact[k] % MOD * inv_fact[n-k] % MOD;}
구현
#include <bits/stdc++.h>using namespace std;const int MOD = 1e9 + 7;int main() { int n; cin >> n; // 파스칼 삼각형 출력 vector<long long> C(n + 1, 0); C[0] = 1; for (int i = 0; i < n; i++) { vector<long long> nxt(n + 1, 0); for (int j = 0; j <= i; j++) { nxt[j] = (nxt[j] + C[j]) % MOD; nxt[j + 1] = (nxt[j + 1] + C[j]) % MOD; } C = nxt; for (int j = 0; j <= i + 1; j++) cout << C[j] << " \n"[j == i + 1]; } // C(n, k) 출력 int k; cin >> k; cout << C[k] << "\n"; return 0;}
MOD = 10**9 + 7def pascal(n): C = [0] * (n + 1) C[0] = 1 rows = [] rows.append(C[:1]) for i in range(1, n + 1): nxt = [0] * (n + 1) for j in range(i + 1): if j > 0: nxt[j] = (nxt[j] + C[j-1]) % MOD nxt[j] = (nxt[j] + C[j]) % MOD C = nxt rows.append(C[:i+1]) return C, rowsn = int(input())C, rows = pascal(n)for row in rows: print(*row)k = int(input())print(C[k])
stdin
52
결과
11 11 2 11 3 3 11 4 6 4 11 5 10 10 5 110
성질
성질
수식
의미
행 합
∑{k=0}{n}(n{)}{k}=2n
n 개 원소 부분집합 수
홀짝 행 합
∑{k even}(n{)}{k}=2{n−1}
짝수 선택 수
Hockey stick
∑{i=r}{n}(i{)}{r}=(n{)+1}{r+1}
대각선 합
Vandermonde
∑{k}(m{)}{k}(n{)}{r−k}=(m{)+n}{r}
합성
대칭
(n{)}{k}=(n{)}{n−k}
삼각형 대칭
복잡도
방법
시간
공간
용도
2D DP
O(N2)
O(N2)
소규모, n 전체 테이블
1D DP
O(N2)
O(N)
공간 절약
팩토리얼 전처리
O(N) 전처리, O(1) 쿼리
O(N)
다중 쿼리, 대규모
Lucas’ Theorem
O(logpN) 쿼리
O(p)
n 극히 크고 소수 MOD
함정
WARNING
MOD 로 나눈 후 나눗셈 불가: (n{)}{k}=f{act[n]}{fact[k]⋅fact[n−k]} MOD 세계에서는 직접 나눗셈 대신 모듈러 역원 사용.
WARNING
k > n 이면 0: 경계 조건 (n{)}{k}=0(k>n 또는 k\<0) 를 반드시 처리.
CAUTION
오버플로우: fact[n] 을 long long 으로 선언해도 n 이 크면 fact[n] * inv_fact[k] 중간에 오버플로우. 각 곱셈마다 % MOD 적용.
💬 댓글