백준: Class 5 - 9527, 10775, 12015

2 분 소요


</br> 클래스 5 계속
</br>

9527: 1의 개수 세기

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

long long dp[54];

long long func(long long a){
    long long ret = a & 1;
    for(int i = 54; i > 0; i--){
        if( a & (1LL << i) ){
            ret += dp[i-1] + (a - (1LL << i) + 1);
            a -= 1LL << i;
        }
    }
    return ret;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    long long a, b;
    cin >> a >> b;

    dp[0] = 1;
    for(int i = 1; i < 54; i++){
        dp[i] = 2*dp[i-1] + (1LL << i);
    }

    cout << func(b) - func(a-1) << endl;
}

와~ 비트 연산이란…
dp 구하기는 했는데 그 뒤를 잘 모르겠어서 다른 분 풀이를 봤다
얘도 누적합 구해서 빼주는 거다. dp[i] = 2^i일 때 1의 개수 누적합
그래서 만약 11101101을 예로 하면

ex) 11101101
11101101 = 1101101 | 10000000
 1101101 =  101101 |  1000000
  101101 =    1101 |   100000
    1101 =     101 |     1000
     101 =       1 |      100

처럼 계산한다.
</br>

10775: 공항

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

struct UnionFind{
    vector<int> parent;
    UnionFind(int n) : parent(n+1){
        for(int i = 0; i < n+1; i++){
            parent[i] = i;
        }
    }
    int f(int u){
        if( u == parent[u] ) return u;
        return parent[u] = f(parent[u]);
    }
    void merg(int u, int v){
        u = f(u), v = f(v);
        parent[u] = v;
    }
};

// in main()
    for(int i = 0; i < p; i++){
        int a;
        cin >> a;
        int pa = uf.f(a);
        if( pa == 0 ) break;
        uf.merg(pa, pa-1);
        cnt++;
    }
    cout << cnt << endl;

그냥 그리디 하면 시간 초과 날 거 같은데 뭐지
이것도 union find를 사용하는 거라고 한다… 구별을 잘 못하겠네
채울 수 있는 만큼 큰 수로 채우기 때문에 그리디랑 복합된 거긴 하다.

0 1 2 3 4
0 1 2 3 4

4 도착
0 1 2 3 4
0 1 2 3 3

2 도착
0 1 2 3 4
0 1 1 3 3

2 도착
0 1 2 3 4
0 0 0 3 3

1 도착
0 1 2 3 4
0 0 0 3 3
1의 부모 == 0이므로 끝


만약 4가 도착하면
4의 부모: 3이므로 3과 2 merge
0 1 2 3 4
0 0 0 0 3

0 1 2 3 4
0 0 0 0 0
경로 압축


</br>

12015: 가장 긴 증가하는 부분 수열 2

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

    ind.push_back(nums[0]);
    for(int i = 1; i < n; i++){
        if( nums[i] > ind.back() ) ind.push_back(nums[i]);
        else{
            auto it = lower_bound(ind.begin(), ind.end(), nums[i]);
            *it = min(*it, nums[i]);
        }
    }

    cout << ind.size() << endl;

자꾸 시간 초과 나서 찾아 봤는데, 알고리즘은 생각한 게 맞았는데 이분 탐색 구현을 잘못한 것 같다 난 이분 탐색을 잘 못하나 봐…
lower bound 저거 쓰니 편하니 좋네

아무튼 전에 비슷한 문제 풀었었는데,

dp[i] = i번째 수에서 가능한 가장 긴 수열의 크기

따라서 i-1번째까지 다 검색해서 가능하면 +1 함.

얘는 n이 짱 커서 그렇게 풀면 시간 초과 난다
그래서,

ex) 10 20 30 9 21 50

ind: 10
ind: 10 20
ind: 10 20 30
ind:  9 20 30   < 10보다 9가 작으므로 바꿈
ind:  9 20 21   < 30보다 21이 작으므로 바꿈
ind:  9 20 21 50

이렇게 수열의 크기를 인덱스로 하고, 그 값은 가능한 값들 중 가장 작은 녀석으로 저장했다
지금 보니 어차피 작은 애들끼리 얘기라 안 바꿔도 될 거 같기도 하네
</br>


열심히…
</br>

댓글남기기