본문 바로가기

BOJ

[BOJ 23578] 비 오는 날

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

 

23578번: 비 오는 날

피에스고등학교의 교장 이환이는 학교를 매우 사랑한다. 그래서 이환이는 매일 점심시간에 학교의 모든 건물을 돌아다닌다. 비가 오는 어느 날, 이환이는 학교 곳곳을 돌아다니고 싶었지만 비

www.acmicpc.net

Tag : greedy

 

문제요약

스패닝 트리를 만드는데 \( \sum{ (degree_i)^2 * a_i } \)를 최소화해라

 

풀이

처음에 이상한 전략을 많이 생각했다.

일단 \(a_i\)에 비례하게 분배해놓고 이득이 되는 쪽으로 옮긴다던지....

그런데 이걸 잘 정리하다보니까 그냥 처음부터 최소화되는 쪽으로 이어가는게 낫다는 결론이 나왔다.

 

일단 모든 정점에서 \( degree_i \)가 1 이상이라면 스패닝 트리는 무조건 만들 수 있다.

그럼 나머지 N - 2개의 degree를 어떻게 분배하냐는 간단하다. 

\(degree_i \)을 1 증가시켰을 때 증가율은 \(2 degree_i + 1 \)인데

그때그때 이것이 작은 순으로 넣어주면 된다.

 

그때그때의 증가율이 작은순으로 넣은 것보다 작은 해가 존재한다고 가정하자.

증가율이 작은 순대로 만든 최소 비용을 ans라고 하면 여기서 증가율이 작은 것을 다른 것으로 대체했을 때

이 비용이 ans보다 작아야한다. i번 정점의 degree를 하나 줄이고 그것이 아닌 증가율이 가장 작지 않은 j를 삽입한다 했을 때

이 때의 답은 \(ans + (-2degree_i + 1) + (2degree_j + 1) \)이 된다. 

이것이 ans보다 작으려면 \(2degree_i - 1 > 2degree_j + 1\)이어야하는데 이것을 만족하려면 위의 가정에 모순된다.

따라서 위 그리디가 성립한다.

 

전체코드

#include "bits/stdc++.h"
#define endl '\n'

using namespace std;
using ll = long long;

ll n;
ll a[303030];
ll b[303030];
priority_queue<pair<ll, ll>> pq;

#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
template<typename T>
struct FASTIO {
    char *p,O[2000000],*d;
    void set() {
        struct stat st;fstat(0, &st);d=O;
        p=(char*)mmap(0,st.st_size,1,1,0,0);
    }
    ~FASTIO() {
        write(1,O,d-O);
    }
    inline T get() {
        T x=0;bool e;p+=e=*p=='-';
        for(char c=*p++;c&16;x=10*x+(c&15),c=*p++);
        return e?-x:x;
    }
    inline void put(int x) {
        if(x<0) *d++='-', x=-x;
        char t[16], *q=t;
        do *q++=x%10|48; while(x/=10);
        do *d++=*--q; while(q!=t);
        *d++=10;
    }
};

FASTIO<int> IO;

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
//    freopen("input.txt", "r", stdin);

    IO.set();
    n = IO.get();
    if(n == 1) return cout << 0 << endl, 0;
    for(int i=1; i<=n; i++) {
        a[i] = IO.get(); b[i] = 1;
        pq.push({-3 * a[i], i});
    }
    for(int t=0; t<n-2; t++) {
        auto [x, idx] = pq.top(); pq.pop();
        b[idx]++;
        pq.push({-(2 * b[idx] + 1) * a[idx], idx});
    }
    ll ans = 0;
    for(int i=1; i<=n; i++) {
        ans += a[i] * b[i] * b[i];
    }
    cout << ans << endl;
}

'BOJ' 카테고리의 다른 글

[BOJ 8903] 장비  (0) 2022.01.13
[BOJ 16474] 이상한 전깃줄  (0) 2022.01.13
[BOJ 10523] 직선 찾기  (0) 2022.01.11
[BOJ 13255] 동전 뒤집기  (0) 2022.01.11
[BOJ 13257] 생태학  (0) 2022.01.11