백준 1407번, 2로 몇 번 나누어질까 풀이
2021. 1. 17. 19:01ㆍProblem Solving/백준
문제
풀이
구간 $[A, B]$를 1씩 증가 시켜보면서 확인하는 방법이 가장 확실하지만, 수의 제한이 무려 $10^{15}$이니 더 빠른 방법을 찾아야 합니다.
우리가 관심 있는 것은 구간 $[A, B]$에서 $2^k$의 배수의 개수입니다.
어떤 수, $k$가 주어졌을 때, 구간 $[A,B]$ 내에서 $k$의 배수의 개수는 $B/k-(A-1)/k$라는 수식을 통해 $O(1)$로 구할 수 있습니다. (단, $A \leq B$)
두번째, $2^{k+1}$의 배수는 모두 $2^k$의 배수입니다. 예를 들면, $32(2^5)$과 같은 수는 $16(2^4)$의 배수임에 동시에 $8, 4, 2, 1$의 배수이기도 합니다. 즉, $2^k$의 배수는 모두 $2^i (i=0,1,...,k)$의 배수라고 나타낼 수 있습니다.
정리하면, 구간 $[A,B]$에서 $2^k\ (k=0,1,2,...,50)$의 배수의 개수를 구하되, 중복되는 수는 빼야 합니다.
소스코드
#include <bits/stdc++.h>
#define all(v) v.begin(), v.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef unordered_map<int, int> mpii;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll a, b;
cin >> a >> b;
--a;
ll ans = 0, sum = 0;
for (ll i = 50; i >= 0; i--) { //중복 제거를 위해 위에서부터 봐줌
ll curr = 1LL << i; //2^i 계산, 오버플로 유의
ll val = b / curr - a / curr; //[a, b] 사이에서 2^i 배수의 개수를 구함
val -= sum; //중복 제거
sum += val; //2^i의 배수를 2^(i-1), 2^(i-2)...에서도 계산하지 않도록 뺌
ans += val * curr; //중복이 제거된 배수의 개수 * 2^i
}
cout << ans;
}
'Problem Solving > 백준' 카테고리의 다른 글
백준 2560번, 짚신벌레 풀이 (0) | 2021.01.26 |
---|---|
백준 10986번, 나머지 합 풀이 (0) | 2021.01.23 |
백준 1943번, 동전 분배 풀이 (0) | 2021.01.11 |
백준 1823번, 수확 풀이 (0) | 2021.01.09 |
백준 1939번, 중량제한 풀이 (0) | 2021.01.07 |