문제 정보
- 문제:
https://www.acmicpc.net/problem/5051
- 제목: 피타고라스의 정리
- 요약:
1 ≤ a,b,c ≤ n−1
,a ≤ b
에서 \(a^2 + b^2 \equiv c^2 \pmod n\)를 만족하는 삼중쌍 개수를 출력합니다.n
은 최대 500,000입니다. - 제한: 시간 1초, 메모리 128MB, 입력 하나
n (2 ≤ n ≤ 500000)
입출력 형식/예제
입력
|
|
출력
|
|
또는
입력
|
|
출력
|
|
접근 개요(아이디어 스케치)
- 핵심 아이디어는 제곱 나머지 분포를 이용한 합성입니다.
cntSq[r] = |{x ∈ [1..n−1] : x^2 ≡ r (mod n)}|
.A = cntSq
라 두고,A ⊛ A
의 순환 컨볼루션은 \((a^2 \bmod n) + (b^2 \bmod n)\)의 분포를 줍니다.
- 순서가 있는 (a,b) 쌍의 총 개수:
ordered = Σ_r (A ⊛ A)[r] · A[r]
(여기서r = c^2 mod n
). a=b
인 대각선 개수:equal = Σ_r A[r] · A[(2r) mod n]
.- 우리가 원하는
a ≤ b
개수는 대칭 보정으로 \(\frac{ordered + equal}{2}\) 입니다. - 순환 컨볼루션은 FFT로 선형 컨볼루션을 계산한 뒤 길이
n
에서 접기(folding)로 구현합니다:circ[k] = conv[k] + conv[k+n]
.
flowchart TD A[제곱 나머지 분포 A[r]] --> B[선형 컨볼루션 A*A (FFT)] B --> C[길이 n에서 접기(순환 컨볼루션)] C --> D[ordered = Σ_r circ[r]·A[r]] A --> E[equal = Σ_r A[r]·A[2r mod n]] D --> F[정답 = (ordered + equal)/2] E --> F
알고리즘 설계
cntSq
를 선형 시간에 구성:x=1..n-1
에 대해r=(x*x) mod n
,cntSq[r]++
.- 길이
L=2^k ≥ 2n
로 zero-padding 하여 FFT 기반 선형 컨볼루션conv=A*A
계산. - 순환 컨볼루션 복원:
circ[k] = conv[k] + conv[k+n] (0 ≤ k < n)
. ordered = Σ_k circ[k]·A[k]
,equal = Σ_r A[r]·A[(2r) mod n]
.answer = (ordered + equal)/2
를 128비트 누적으로 출력.
올바름 근거(요지):
A⊛A
는 \((a^2 mod n)+(b^2 mod n)\)의 분포를 정확히 세며,c
선택은 같은 나머지r
의 개수A[r]
만큼 독립 곱으로 곱해집니다.- 순서쌍(ordered)에서
a≠b
는 쌍대가 존재하므로 2배,a=b
는 1배이므로a≤b
변환식U=(O+E)/2
가 성립합니다.
복잡도
- 시간: \(O(n \log n)\) (FFT 2회 + 역변환 1회)
- 공간: \(O(n)\)
구현 (C++)
|
|
코너 케이스 체크리스트
- 작은 n (예: 2, 3, 4, 5, 6, 7)에서 직접 검증값과 비교
a=b
대각선 보정:equal = Σ_r A[r]·A[2r mod n]
식을 별도 확인- 부동소수점 반올림:
llround
사용 및 음수 소수 에러 가드(max(·,0)
) 적용 - 누적 오버플로: 합산은
__int128
사용, 출력 시 자리수 단위 변환
제출 전 점검
- 입력/출력 형식 및 개행 확인
- FFT 길이
L
은 항상2n
이상인 2의 거듭제곱인지 확인 - 순환 컨볼루션 접기(
conv[k] + conv[k+n]
) 구현 확인 - 시간/메모리 여유:
n=5e5
에서도 길이≈1,048,576
FFT 3회 내 처리
참고자료
- 순환 컨볼루션과 접기: 선형 컨볼루션 결과를 길이
n
에서 접어 합산 - 대칭 보정:
U = (O + E) / 2
(ordered→a≤b
변환)