알고리즘 문제해결/BOJ

BOJ 15224 EvenOdd

havana723 2022. 12. 5. 11:21

https://www.acmicpc.net/problem/15224

 

15224번: EvenOdd

Consider the following function f(X), which takes a single positive integer as argument, and returns an integer. function f(X): iterations := 0 while X is not 1: if X is even: divide X by 2 else: add 1 to X add 1 to iterations return iterations It can be s

www.acmicpc.net

 

어떤 정수 $x$에 특정한 연산을 반복해서 취할 때, $x$가 1일 될 때까지 취해야 하는 연산의 갯수를 $f(x)$라고 정의해봅시다. 이 때 두 수 $l$과 $r$이 주어졌을 때, $f(l) + f(l + 1) + \cdots + f(r - 1) + f(r)$의  값을 구해야 합니다. 이런 식의 문제를 풀 때 유효하게 생각해 볼 수 있는 접근 방법이 있는데, $f(1) + f(2) + \cdots + f(x-1) + f(x)$을 $g(x)$라고 할 때 $g(r) - g(l-1)$로 문제를 치환하는 것입니다. $g(x)$를 빠르게 구할 수 있다면, 우리가 찾고자 하는 답도 빠르게 구할 수 있다는 뜻이 됩니다.

 

그렇다면 이 문제에서 $g(x)$를 어떻게 빠르게 구할 수 있을까요? 저도 그렇고 제 주변의 여러 사람들도 그랬듯이 2를 나누는 연산을 여러번 반복하니까 2진법으로 나타냈을 때의 비트를 가지고 어떻게 잘 하면 되지 않을까 싶어집니다. 하지만 이렇게 접근하면 정답에 다가가기 상당히 어려워집니다. 이 문제의 경우 2진법으로 접근하기보다는 홀짝을 나눠서 생각하는 접근을 통해 풀 수 있습니다.

 

$g(x)$를 풀어서 쓰면 (당연하게도) $f(1) + f(2) + \cdots + f(x-1) + f(x)$입니다. 이를 홀짝을 나눠서 적어보면 ($x$가 짝수일 경우) $f(1) + f(3) + \cdots + f(x - 3) + f(x-1)$와 $f(2) + f(4) + \cdots + f(x-2) + f(x)$를 더한 값입니다. 이 때 1을 제외한 임의의 홀수 $k$에 대하여 $f(k) = f(k+1) + 1$이 됩니다. 이를 이용해 앞의 식을 정리하면 $(0 + f(4) + \cdots + f(x-2) + f(x) + (x / 2 + 1))$ $+$ $(f(2) + f(4) + \cdots + f(x-2) + f(x))$ $=$ $0 + (f(4) + \cdots + f(x-2) + f(x)) *2 + f(2)$라는 식이 나옵니다. 이 식을 재귀적으로 계산해주면 답을 구할 수 있습니다.

 

아래는 코드입니다.

 

#include <bits/stdc++.h>

#define FastIO ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
#define ends ' '
#define int long long

const int MOD = 1e9 + 7;

using namespace std;

int T, l, r;

int calc(int x) {
    if (x == 0 || x == 1) return 0;
    if (x == 2) return 1;
    int ret = 0;
    while (x > 1) {
        if (x % 2) x++;
        else x /= 2;
        ret++;
    }
    return ret;
}

int func(int x) {
    if (!x) return 0;
    if (x == 1) return 0;

    int ret = x / 2 - 1;
    ret -= 1;
    ret += ((func(x / 2) % MOD + (x / 2) % MOD) % MOD) * 2;
    if (x % 2) ret += calc(x);

    return ret % MOD;
}

int32_t main(void) {
    FastIO;

#ifdef HN
    freopen("_run/in.txt", "r", stdin), freopen("_run/out.txt", "w", stdout);
#endif

    T = 1;
    //cin >> T;

    while (T--) {
        cin >> l >> r;
        int ans = func(r) - func(l - 1);
        if (ans < 0) ans += MOD;
        cout << ans << endl;
    }

    return 0;
}

'알고리즘 문제해결 > BOJ' 카테고리의 다른 글

BOJ 11562 백양로 브레이크  (0) 2022.11.23
BOJ 9567 Empty Stalls  (0) 2022.11.07
BOJ 17299 오등큰수  (0) 2022.11.03
BOJ 1613 역사  (0) 2022.11.01
BOJ 16929 Two Dots  (0) 2022.10.26