백준 1407번, 2로 몇 번 나누어질까 풀이
2021. 1. 17. 19:01ㆍProblem Solving/백준
문제
풀이
구간 [A,B]를 1씩 증가 시켜보면서 확인하는 방법이 가장 확실하지만, 수의 제한이 무려 1015이니 더 빠른 방법을 찾아야 합니다.
우리가 관심 있는 것은 구간 [A,B]에서 2k의 배수의 개수입니다.
어떤 수, k가 주어졌을 때, 구간 [A,B] 내에서 k의 배수의 개수는 B/k−(A−1)/k라는 수식을 통해 O(1)로 구할 수 있습니다. (단, A≤B)
두번째, 2k+1의 배수는 모두 2k의 배수입니다. 예를 들면, 32(25)과 같은 수는 16(24)의 배수임에 동시에 8,4,2,1의 배수이기도 합니다. 즉, 2k의 배수는 모두 2i(i=0,1,...,k)의 배수라고 나타낼 수 있습니다.
정리하면, 구간 [A,B]에서 2k (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 |