[Gold IV] 2로 몇 번 나누어질까 - 1407
성능 요약
메모리: 11552 KB, 시간: 76 ms
범위가 무려 1≤A≤B≤ 로 엄청나다.
그래서 A ~ B 까지 순회하면서 더할 수는 없다.
💡 1 ~ n까지 수들은 소수 x로 몇번 나누어 떨어질까 ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - 1 - 2 - 1 - 3 - 1 - 2 - 1 - 4 - 1 - 2 1부터 20까지 순회하며 각 수를 소인수분해 했을때 2가 몇 번씩 들어가는지 적어보았다.
이를 2가 들어간 횟수만큼 동그라미로 표현하게 되면 아래 사진과 같이 표시되며 오른쪽과 같은 의미를 가질 수 있다.
위를 참고하여
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
2로 나누어 지지 않는 수(홀수)는 인 2의 거듭제곱 꼴 중 가장 큰 약수를 가진다.
그러면 여기서 홀수의 개수는 어떻게 구할까 ?
-> ceil(20/2) = 10
| 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 |
4로 나누어 지지 않는 수는 인 2의 거듭제곱 꼴 중 가장 큰 약수를 가진다.
그러면 여기서 4로 나누어 지지 않는 수의 개수는 어떻게 구할까 ?
-> ceil(10/2) = 5
| 4 | 8 | 12 | 16 | 20 |
8로 나누어 지지 않는 수는 인 2의 거듭제곱 꼴 중 가장 큰 약수를 가진다.
그러면 여기서 8로 나누어 지지 않는 수의 개수는 어떻게 구할까 ?
-> ceil(5/2) = 3 (이런 경우 때문에 올림을 해야한다)
| 8 | 16 |
16로 나누어 지지 않는 수는 인 2의 거듭제곱 꼴 중 가장 큰 약수를 가진다.
그러면 여기서 8로 나누어 지지 않는 수의 개수는 어떻게 구할까 ?
-> ceil(2/2) = 1
| 16 |
32로 나누어 지지 않는 수는 인 2의 거듭제곱 꼴 중 가장 큰 약수를 가진다.
그러면 여기서 16로 나누어 지지 않는 수의 개수는 어떻게 구할까 ?
-> ceil(1/2) = 1
이렇게 ~ 들을 가장 큰 약수로 가지는 수들의 개수를 각각 알아봤다.
이 수를 다 더하면
1*10 + 2*5 + 4*3 + 8*1 + 16*1 = 56
이며 이 숫자는 1 ~ 20까지의 2의 거듭제곱큰약수들의 합이 된다.
이를 이용하여 1 ~ B 에서 1 ~ A-1을 빼주면 A~B까지의 합을 구할 수 있다.
f(B) - f(A-1)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main_1407 {
static long A;
static long B;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
A = Long.parseLong(st.nextToken());
B = Long.parseLong(st.nextToken());
// A부터 B까지의 수 중 2의 거듭제곱 꼴이면서 가장 큰 약수를 찾아 더하여 출력
System.out.println(twoCount(B) - twoCount(A - 1));
}
/**
* A부터 B까지의 수 중 2의 거듭제곱 꼴이면서 가장 큰 약수를 찾아 더하는 메서드
*
* @param n 약수를 찾을 대상 숫자
* @return 2의 거듭제곱 꼴이면서 가장 큰 약수를 찾아 더한 결과
*/
private static long twoCount(long n) {
long result = 0;
long divisor = 1;
// 2의 거듭제곱 꼴이면서 가장 큰 약수를 찾기 위한 반복문
while (n > 0) {
// 현재의 divisor로 나누어지지 않는 숫자의 가장 큰 약수를 찾아 더함
long indivisibleNumberCnt = n / divisor;
// 결과에 현재 divisor와 가장 큰 약수를 곱하여 누적
result += divisor * indivisibleNumberCnt;
// 다음 거듭제곱으로 이동
divisor *= 2;
}
return result;
}
}
항상 생각의 과정을 공유해 주셔서 감사드립니다~