2018년 6월 6일 수요일

15708 미네크래프트

15708 미네크래프트 https://www.acmicpc.net/problem/15708
Codeforces #470 (Div.2) C - Producing Snow http://codeforces.com/contest/948/problem/C
14452 Cow Dance Show https://www.acmicpc.net/problem/14452
1781 컵라면 https://www.acmicpc.net/problem/1781   (https://lyzqm.blogspot.com/2017/10/1781.html)

pq와 greedy를 이용한 문제들을 소개하고자 한다.

1. 미네크래프트
시간 T와 N개의 바위를 각각 캐는데 걸리는 시간이 주어지고 옆의 바위로 건너가는 시간P가 주어진다.
캘 수 있는 바위의 최대개수를 구하는 문제이다.

현재 보고있는 바위를 캘지 정하는 것으로 풀지 말고 
지금 까지 봤던 바위들 중 작은 바위들을 캐서 현재 보고있는 바위까지 건너올 수 있게끔 풀어야 한다.

이러한 유형의 문제들은 우선적으로 pq에 넣어서 처리해주는 편이 좋다.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n, t, p;
int arr[MAXN];
priority_queue<int> pq;
int main() {
    scanf("%d%d%d", &n, &t, &p);
    for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
    int sum = 0, sze = 0, ans = 0;
    for (int i = 0; i < n; i++) {
        if (t <= p * i) break;
        sze++;
        sum += arr[i];
        pq.push(arr[i]);
        while (!pq.empty() && sum > t) {
            sum -= pq.top();
            sze--;
            pq.pop();
        }
        if (sum > t) break;
        ans = max(ans, sze);
        sum += p;
    }
    printf("%d\n", ans);
    return 0;
}
cs

2. Producing Snow 
N일 동안 눈이 $s_{i}$만큼 쌓인다.
각각의 날에 기온은 $t_{i}$이다.
눈이 녹는 정도는 그날 기온에 영향을 받으며 이전에 남았던 눈 또한 녹는다.
각각의 날에 녹는 눈의 양을 구하는 문제이다.


대회때는 못풀었던 아쉬운 문제이다.
pq에 $s_{i}$와 이전에 기온들의 합을 넣어 관리할 수 있다.
눈이 완전히 녹는것은 이전 기온들의 합과 현재 기온의 합보다 작거나 같아지는 경우이다.
아직 pq에 남아있는 크기만큼 눈들 또한 더 녹기 때문에 이것도 더해줘야한다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
const int MAXN = 1e5 + 5;
int arr[MAXN];
priority_queue<ll> pq;
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
    ll s = 0;
    for (int i = 0, minus; i < n; i++) {
        ll ret = 0;
        scanf("%d", &minus);
        pq.push(-(arr[i] + s));
        s += minus;
        while (!pq.empty() && -pq.top() <= s) {
            ret += -pq.top() - s + minus;
            pq.pop();
        }
        ret += pq.size() * minus;
        printf("%lld ", ret);
    }
    return 0;
}
cs

3. Cow Dance Snow
소들이 춤추는 시간들이 정해져있고 춤을 다 춘 소는 순차적으로 들어간다.
모든 소가 춤을 추는데 걸리는 시간 T를 넘지 않도록 소들이 춤출 수 있는 
최소 사이즈 K를 구하는 문제다. 

이분탐색과 위의 문제처럼 pq를 이용한 기법으로 해결할 수 있다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 6;
int n, t;
ll arr[MAXN];
bool ispossible(int idx) {
    ll prv = 0, sum = 0;
    priority_queue<ll> pq;
    for (int i = 0; i < idx; i++) pq.push(-arr[i]);
    while (!pq.empty()) {
        ll curr = -pq.top();
        pq.pop();
        sum += (curr - prv);
        if (sum > t) return false;
        prv = curr;
        if (idx < n) pq.push(-sum - arr[idx++]);
    }
    return sum <= t;
}
int main() {
    scanf("%d%d", &n, &t);
    for (int i = 0; i < n; i++) scanf("%lld", &arr[i]);
    
    int l = 1, r = n;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (ispossible(mid)) r = mid - 1;
        else l = mid + 1;
    }
    printf("%d\n", l);
    return 0;
}
cs

2018년 6월 1일 금요일

15790 최종병기 활

15790 최종병기 활 https://www.acmicpc.net/problem/15790

고무줄이 주어진다. 고무줄의 둘레는 $n$이며 $m$개의 홈이 파져있다. 
고무줄을 잘라 $k$겹의 고무줄 중 가장 작은 고무줄의 길이를 최대화하는 문제이다.

순수 greedy만 생각을 했었는데 이분탐색으로 원의 기준을 돌리면서 $O(m^2 log n)$에 해결할 수 있다.

원의 기준을 돌리면서 arr배열에 다시 넣었는데 배열 크기를 2$m$으로 잡고 idx값만 바꾸면서 돌려줄 수 있다. 
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n, m, k;
int arr[MAXN];
int sub[MAXN];
bool ispossible(int mid) {        // 모든 part >= mid인지 판별
    for (int i = 0; i < m; i++) {
        int t = 0;
        int idx = 0;
        for (int j = i; j < m; j++) arr[idx++] = sub[j];
        for (int j = 0; j < i; j++) arr[idx++] = sub[j];
        int here = 0;
        for (int j = 0; j < m; j++) {
            if (here < mid) here += arr[j];
            if (here >= mid) {
                here = 0;
                t++;
            }
        }
        if (here >= mid) t++;
        if (t >= k) return true;
    }
    return false;
}
int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 0; i < m; i++) scanf("%d", &arr[i]);
    for (int i = 0; i < m - 1; i++) sub[i] = arr[i + 1] - arr[i]; sub[m - 1] = n - arr[m - 1] + arr[0];
    int l = 1, r = n;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (ispossible(mid)) l = mid + 1;
        else r = mid - 1;
    }
    printf("%d\n", r);
    return 0;
}
cs

2018년 4월 6일 금요일

13997 이항 계수와 쿼리

13997 이항 계수와 쿼리 https://www.acmicpc.net/problem/13977

$M$개의 자연수 $N$과 정수 $K$가 주어질 때 $_{n}C_{r}$을 구하는 문제이다.

$O(N + log(p) + M)$에 해결할 수 있다.
$_{n}C_{r}$ = $n!/(r!*(n-r)!$ = $n!*(r!*(n-r)!)^{-1}$ = $n!*(r!)^{-1}*(n-r)!^{-1}$

$a^{p}$ $\equiv$ $a$ $(mod$ $p)$ ($a$는 자연수, $p$는 소수)
$a^{p - 1}$ $\equiv$ $1$ $(mod$ $p)$
$a^{p - 2}*a$ $\equiv$ $1$ $(mod$ $p)$ 즉, $a$의 역원은 $a^{p-2}$이다.
$a^{p-2}$는 분할정복으로 $log(p)$만에 구할 수 있고 역원들 또한 전처리해주어 $O(n)$에 해결할 수 있다.


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod (ll)(1e9 + 7)
const int MAXN = 4e6 + 6;
int n, r;
ll fact[MAXN];
ll inverse[MAXN];
ll mpow(ll a, ll p) {
    if (p == 0) return 1;
    if (p == 1) return a;
    if (p & 1) return mpow(a, p - 1)*a % mod;
    ll tmp = mpow(a, p / 2);
    return tmp * tmp % mod;
}
void init() {
    fact[0] = fact[1] = 1;
    for (int i = 2; i < MAXN; i++) fact[i] = fact[i - 1] * i % mod;
    inverse[MAXN - 1] = mpow(fact[MAXN - 1], mod - 2);
    for (int i = MAXN - 2; i >= 1; i--) inverse[i] = inverse[i + 1] * (i + 1) % mod;
    inverse[0] = inverse[1] = 1;
}
ll nCr(ll n, ll r) {
    return ((fact[n] * inverse[r] % mod) * inverse[n - r]) % mod;
}
int main(){
    init();
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &r);
        printf("%lld\n", nCr(n, r));
    }
    return 0;
}
cs