일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
Tags
- 스프링
- 플러터
- php 프로그래밍 입문 3판
- 백준
- php 프로그래밍 입문
- 페이코 초대코드
- 자바 스프링
- C
- php 프로그래밍 입문 예제
- C언어
- php 프로그래밍
- 최단 경로
- SWEA
- 배열
- programmers
- 한정 분기
- 페이코 친구코드
- Flutter
- 파이썬
- Java
- 플러터 개발환경 설정
- 자바
- 페이코 추천인
- 페이코 추천인코드
- spring
- php 프로그래밍 입문 문제풀이
- php 프로그래밍 입문 연습문제
- php
- php 프로그래밍 입문 솔루션
- JAVA SPRING
Archives
- Today
- Total
03-10 07:08
ImJay
[BOJ/Java] 11401. 이항 계수 3 본문
반응형
[BOJ/Java] 11401. 이항 계수 3
https://www.acmicpc.net/problem/11401
문제 해석
이항 계수 3 문제는 주어진 자연수 \(N\)과 \(K\)에 대해 이항 계수 \(\binom{N}{K}\)를 \(1,000,000,007\)로 나눈 나머지를 구하는 문제이다. 이항 계수는 다음과 같이 정의된다: \[ \binom{N}{K} = \frac{N!}{K!(N-K)!} \] 이 문제는 \(N\)과 \(K\)가 매우 크기 때문에(\(1 \leq N \leq 4,000,000\), \(0 \leq K \leq N\)), 직접 계산하는 대신 **페르마의 소정리**와 **모듈로 연산**을 활용하여 효율적으로 해결해야 한다.
풀이 과정
- 페르마의 소정리 적용: 페르마의 소정리에 따르면, 소수 \(p\)에 대해 \(a^{p-1} \equiv 1 \ (\text{mod} \ p)\)이다. 이를 활용하여 분모의 역원을 계산한다: \[ \frac{1}{K!(N-K)!} \equiv (K!(N-K)!)^{p-2} \ (\text{mod} \ p) \]
- 팩토리얼 계산: \(N!\), \(K!\), \((N-K)!\)를 미리 계산한다. 이때, 모듈로 \(1,000,000,007\)을 적용하여 오버플로를 방지한다.
- 역원 계산: \(K!\)와 \((N-K)!\)의 역원을 페르마의 소정리를 통해 계산한다.
- 이항 계수 계산: \(N! \times (K!)^{-1} \times ((N-K)!)^{-1} \ (\text{mod} \ p)\)를 계산하여 최종 결과를 구한다.
코드
import java.io.*;
import java.util.*;
public class Main {
static final long MOD = 1_000_000_007;
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 K = Integer.parseInt(st.nextToken());
// 팩토리얼 계산
long[] factorial = new long[N + 1];
factorial[0] = 1;
for (int i = 1; i <= N; i++) {
factorial[i] = (factorial[i - 1] * i) % MOD;
}
// 역원 계산 (페르마의 소정리)
long denominator = (factorial[K] * factorial[N - K]) % MOD;
long inverseDenominator = pow(denominator, MOD - 2);
// 이항 계수 계산
long answer = (factorial[N] * inverseDenominator) % MOD;
System.out.println(answer);
}
// 분할 정복을 이용한 거듭제곱 계산
static long pow(long base, long exponent) {
if (exponent == 1) return base % MOD;
long half = pow(base, exponent / 2);
long result = (half * half) % MOD;
if (exponent % 2 == 1) result = (result * base) % MOD;
return result;
}
}
시간 복잡도 분석
시간 복잡도는 O(N + log MOD)이다. 팩토리얼을 계산하는 데 O(N), 역원을 계산하는 데 O(log MOD)의 시간이 소요되기 때문이다. 이는 문제의 제약 조건 내에서 효율적으로 동작한다.
느낀점
이 문제는 페르마의 소정리와 모듈로 연산을 활용하여 이항 계수를 효율적으로 계산하는 방법을 배울 수 있는 좋은 문제이다. 특히, 큰 수의 팩토리얼과 역원을 계산하는 과정에서 수학적 지식의 중요성을 느낄 수 있었다. 이러한 문제는 조합론이나 암호학 등 다양한 분야에서 활용될 수 있을 것 같다.
반응형
'알고리즘 > BOJ - Java' 카테고리의 다른 글
[BOJ/Java] 2933. 미네랄 (0) | 2025.03.09 |
---|---|
[BOJ/Java] 10830. 행렬 제곱 (0) | 2025.03.09 |
[BOJ/Java] 1655. 가운데를 말해요 (0) | 2025.03.08 |
[BOJ/Java] 12865. 평범한 배낭 (0) | 2025.03.08 |
[BOJ/Java] 3197. 백조의 호수 (0) | 2025.03.08 |
Comments