핵심 통찰: a^n - b^n의 p-진 값을 a-b의 p-진 값과 n의 p-진 값으로 분해.
시각화
핵심 아이디어
Case 1: p가 홀수, p | a-b, p ∤ a, p ∤ b
v_p(a^n - b^n) = v_p(a - b) + v_p(n)
증명 스케치: 수학적 귀납법으로 v_p(a^p - b^p) = v_p(a - b) + 1을 보이고, 일반 n에 대해 인수분해와 반복 적용.
Case 2: p = 2, 2 | a-b, a, b 홀수
v_2(a^n - b^n) = v_2(a - b) + v_2(a + b) + v_2(n) - 1 (n >= 1)
Case 3: p | a+b (a^n + b^n, n 홀수)
v_p(a^n + b^n) = v_p(a + b) + v_p(n) (p 홀수, p | a+b, p ∤ a, p ∤ b)
핵심 공식
조건
결과
p 홀수, p|a-b, p∤ab
v_p(a^n - b^n) = v_p(a-b) + v_p(n)
p=2, 2|a-b, a,b 홀수
v_2(a^n - b^n) = v_2(a-b) + v_2(a+b) + v_2(n) - 1
p 홀수, p|a+b, p∤ab, n 홀수
v_p(a^n + b^n) = v_p(a+b) + v_p(n)
알고리즘
// p-진 값 계산v_p(x): cnt = 0 while x % p == 0: cnt += 1 x //= p return cnt// LTE mainlte(p, a, b, n): if p == 2 and (a - b) % 2 == 0 and a % 2 == 1 and b % 2 == 1: return v_2(a - b) + v_2(a + b) + v_2(n) - 1 if (a - b) % p == 0 and a % p != 0 and b % p != 0: return v_p(a - b) + v_p(n) if n % 2 == 1 and (a + b) % p == 0 and a % p != 0 and b % p != 0: return v_p(a + b) + v_p(n) // fallback: 직접 계산 (a^n - b^n) mod p^k return v_p(pow(a, n) - pow(b, n))
구현
// Lifting The Exponent Lemma#include <bits/stdc++.h>using namespace std;using ll = long long;int vp(ll x, int p) { int cnt = 0; while (x % p == 0) { cnt++; x /= p; } return cnt;}ll modpow(ll a, ll n, ll mod) { ll r = 1; while (n) { if (n & 1) r = r * a % mod; a = a * a % mod; n >>= 1; } return r;}int lte(int p, ll a, ll b, ll n) { if ((a - b) % p != 0) { if (n % 2 == 1 && (a + b) % p == 0 && a % p != 0 && b % p != 0) return vp(a + b, p) + vp(n, p); return 0; } if (a % p == 0 || b % p == 0) return 0; if (p == 2) { if (a % 2 == 0 || b % 2 == 0) return 0; return vp(a - b, 2) + vp(a + b, 2) + vp(n, 2) - 1; } return vp(a - b, p) + vp(n, p);}int main() { int p; ll a, b, n; cin >> p >> a >> b >> n; int ans = lte(p, a, b, n); cout << ans << "\n"; // verify ll mod = 1; for (int i = 0; i < ans + 2; i++) mod *= p; ll diff = (modpow(a, n, mod) - modpow(b, n, mod) + mod) % mod; int act = vp(diff, p); cout << "v_p(a^n-b^n) = " << act << "\n";}
# Lifting The Exponent Lemmaimport sysinput = sys.stdin.readlinedef vp(x, p): cnt = 0 while x % p == 0: cnt += 1 x //= p return cntdef modpow(a, n, mod): r = 1 while n: if n & 1: r = r * a % mod a = a * a % mod n >>= 1 return rdef lte(p, a, b, n): if (a - b) % p != 0: if n % 2 == 1 and (a + b) % p == 0 and a % p != 0 and b % p != 0: return vp(a + b, p) + vp(n, p) return 0 if a % p == 0 or b % p == 0: return 0 # LTE condition fails if p == 2: if a % 2 == 0 or b % 2 == 0: return 0 return vp(a - b, 2) + vp(a + b, 2) + vp(n, 2) - 1 return vp(a - b, p) + vp(n, p)p, a, b, n = map(int, input().split())ans = lte(p, a, b, n)print(ans)# verifymod = p ** (ans + 2)diff = (modpow(a, n, mod) - modpow(b, n, mod)) % modact = vp(diff, p)print(f"v_p(a^n-b^n) = {act}")
stdin
3 4 1 9
결과
3v_p(a^n-b^n) = 3
stdin
2 5 3 4
결과
3v_p(a^n-b^n) = 3
복잡도
항목
값
LTE 판정
O(log_p(min(a,b)) + log n)
v_p 계산
O(log_p x)
검증 (분할 정복 거듭제곱)
O(log n)
공간
O(1)
변형 / 활용
1. a^n + b^n (n 홀수)
p | a+b, p ∤ a, p ∤ b, n 홀수이면 v_p(a^n + b^n) = v_p(a+b) + v_p(n).
2. a^n - b^n where p | a-b but p | a
p가 a나 b를 나누면 조건 불충족. 다른 소인수 분석 필요.
3. 연립 LTE
여러 소인수에 대해 각각 LTE 적용 후 곱. 예: m = product of p_i^(e_i) 에 대해 m | a^n - b^n 조건.
💬 댓글