백준: Class 6 - 14428, 15824

3 분 소요


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

14428: 수열과 쿼리 16

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

#include <bits/stdc++.h>
#define MOD 1000000007

using namespace std;

int nums[100001];
vector< pair<int, int> > segtree;

pair<int, int> makeSeg(int a, int b, int now){
    if( a == b ) return segtree[now] = make_pair(nums[a], a);
    int mid = (a+b) / 2;
    return segtree[now] = min(makeSeg(a, mid, now*2), makeSeg(mid+1, b, now*2+1));
}

pair<int, int> minSeg(int a, int b, int now, int l, int r){
    if( r < a || b < l ) return make_pair(INT_MAX, a);
    if( l <= a && b <= r ) return segtree[now];
    int mid = (a+b) / 2;
    return min(minSeg(a, mid, now*2, l, r), minSeg(mid+1, b, now*2+1, l, r));
}

pair<int, int> updateSeg(int a, int b, int now, int ind, int change){
    if( ind < a || b < ind ) return segtree[now];
    if( a == b ){
        segtree[now].first = change;
        return segtree[now];
    }
    int mid = (a+b) / 2;
    return segtree[now] = min(updateSeg(a, mid, now*2, ind, change), updateSeg(mid+1, b, now*2+1, ind, change));
}

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

    int n;
    cin >> n;

    for(int i = 0; i < n; i++){
        cin >> nums[i];
    }

    int h = ceil(log2(n));
    segtree.assign(1 << (h+1), make_pair(0, 0));
    makeSeg(0, n-1, 1);

    int m;
    cin >> m;

    for(int i = 0; i < m; i++){
        int a, b, c;
        cin >> a >> b >> c;
        if( a == 1 ){
            updateSeg(0, n-1, 1, b-1, c);
            nums[b] = c;
        }
        else{
            cout << minSeg(0, n-1, 1, b-1, c-1).second + 1 << '\n';
        }
    }

}

또 세그먼트 트리
간단하다 전 포스트(2357: 최솟값과 최댓값, https://cyj893.github.io/algorithm/Algorithm17_5/)와 같이 그냥 최솟값 구하면 되는데, 얘는 인덱스를 출력해야 하므로 (최솟값, 최솟값의 인덱스) 페어로 세그먼트 트리에 저장했다.
최솟값이 만약 같다면 인덱스가 작은 순이므로, 그냥 페어 비교로 작은 거 골라 주면 된다.
</br>

15824: 너 봄에는 캡사이신이 맛있단다

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

방법 1.

#include <bits/stdc++.h>
#define MOD 1000000007

using namespace std;

int nums[300001];
int pows[300001];

long long mypow(long long a, int b){
    if( pows[b] ) return pows[b];
    if( b == 0 ) return 1;
    if( b == 1 ) return a;
    if( b % 2 ) return pows[b] = a*mypow(a, b-1) % MOD;
    long long aa = mypow(a, b/2) % MOD;
    return pows[b] = aa*aa % MOD;
}

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

    int n;
    cin >> n;

    for(int i = 0; i < n; i++){
        cin >> nums[i];
    }

    sort(nums, nums+n);

    long long ans = 0;
    for(int i = 0; i < n-1; i++){
        for(int j = i+1; j < n; j++){
            ans += mypow(2, j-i-1) * (nums[j] - nums[i]);
            ans %= MOD;
        }
    }

    cout << ans << endl;

}

어떻게 풀까 보니까 조합에서 최댓값과 최솟값의 차만 구하면 된다.
그래서 정렬해서 보니까,

ex) 백준 예제
1 4 5 5 6 10

현재 조합을 i~j에서 만든다면(i j  포함), 주헌고통지수는 nums[j]-nums[i] 같다.  

1~4: 4-1 = 3 1  -> 2^0
1~5: 5-1 = 4 2  -> 2^1
1~5: 5-1 = 4 4  -> 2^2
...
(nC0 + nC1 + ... + nCn = 2^n이므로)

이렇게 풀면 50점 받는다.
n이 300000이면 포문 두 번 도니까 시간초과 나기 때문…

일단 방향은 맞는 거 같으니까 압축해 보자
</br>

방법 2.

위 예제에서, 더해지는 만큼과 빼지는 만큼을 각 수마다 출력해 봤다.

ex) 백준 예제
1 4 5 5 6 10

1: -1 -2 -4 -8 -16
4:  1 -1 -2 -4 -8
5:  2  1 -1 -2 -4
5:  4  2  1 -1 -2
6:  8  4  2  1 -1
10: 16 8  4  2  1
 1*(-31) + 4*(1-15) + 5*(3-7) + 5*(7-3) + 6*(15-1) + 10*(31) = 307

아하~~ 2^(n-1-i)-1 만큼은 빼지고, 2^i-1 만큼은 더해지는구나

따라서 바꾼 코드는

    for(int i = 0; i < n; i++){
        ans -= (mypow(2, n-i-1) - 1) * nums[i];
        ans %= MOD;
        ans += (mypow(2, i) - 1) * nums[i];
        ans %= MOD;
    }
}

250점 맞았다 굿

</br>


개강해서 슬프다
</br>

댓글남기기