2017년 11월 29일 수요일

1273 샷

1278 샷 https://www.acmicpc.net/problem/1273

N열의 맥주캔이 쌓여져있는 상태가 주어진다.
총으로 하나의 층을 없앨 수 있는데 맥주캔의 종류(검,회,흰)에따라 점수를 획득한다.
M개의 쿼리를 입력받아 해당 층을 없앨 때 점수를 구하는 문제다.

NM이 300,000이나 되고 하나의 열에 최대 3,000,000층 만큼의 맥주가 쌓일 수 있다.
쿼리를 받을때마다 일일이 처리하는건 절대 안되고 굉장히 크기가 큰만큼 메모리도 신경써야한다.

처음에는 segment treelazy propagation로 3,000,000층에서 각층의 점수를 누적해서 저장해주었다.
근데 long long 변수로 저장하고 이러한 segment tree를 2개 저장했으니 8*16*3,000,000 byte가 필요하였다.
그래서 메모리초과가 발생하였다.

좀 생각해보니 각층의 점수 누적을 segment tree로 할 필요없이 일반배열에 저장해서 누적합하면 될것같았다.
그리고 각 층의 쿼리계산은 아까 구현해놓은 segment tree로 갖다 썼다.

사실 메모리초과 발생해서 틀리고 나서 못풀거같은 느낌이 들었는데 차분히 생각하니 답이 나왔다.
또 배열을 int형으로 1200만 선언을 해본적은 처음이고 되는사실도 오늘 알았다.
풀려서 기분이 좋다.
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int N, M;
int Plus[3= { 1,2,5 };
int p[300001];
int sum[3000000];
bool chk[3000000];
struct Segment {
    vector<int> tree;
    int size;
    Segment() {}
    Segment(int N) :size(N) { tree.resize(4 * N, 0); }
    int init(int node, int nl, int nr) {
        if (nl == nr) return tree[node] = 1;
        int mid = (nl + nr) >> 1;
        return tree[node] = init(node * 2, nl, mid) + init(node * 2 + 1, mid + 1, nr);
    }
    void init() {
        init(10size - 1);
    }
    int update(int idx, int val, int node, int nl, int nr) {
        if (nl > idx || nr < idx) return tree[node];
        if (nl == nr) return tree[node] += val;
        int mid = (nl + nr) >> 1;
        return tree[node] = update(idx, val, node * 2, nl, mid) + update(idx, val, node * 2 + 1, mid + 1, nr);
    }
    void update(int idx, int val) {
        update(idx, val, 10size - 1);
    }
    int find(int val, int node, int nl, int nr) {
        if (nl == nr) return nl;
        int mid = (nl + nr) >> 1;
        if (tree[node * 2>= val) return find(val, node * 2, nl, mid);
        else return find(val - tree[node * 2], node * 2 + 1, mid + 1, nr);
    }
    int find(int val) {
        return find(val, 10size - 1);
    }
};
int main() {
    scanf("%d"&N);
    Segment seg(3000000);
    seg.init();
    for (int n = 0;n < 3;n++) {
        for (int m = 0, in;m < N;m++) {
            scanf("%d"&in);
            sum[p[m]] += Plus[n];
            sum[p[m] + in] -= Plus[n];
            p[m] += in;
        }
    }
    for (int n = 0;n < 3000000;n++) {
        if (n != 0) sum[n] += sum[n - 1];
    }
    scanf("%d"&M);
    for (int m = 0, in;m < M;m++) {
        scanf("%d"&in);
        int idx = min(3000000 - 1, seg.find(in));
        if (!chk[idx]) {
            chk[idx] = true;
            printf("%d\n", sum[idx]);
            seg.update(idx, -1);
        }
        else printf("0\n");
    }
    return 0;
}
cs

댓글 1개:

  1. 전 처음 구축할 때 그냥 여사건의 법칙을 이용해서 접근했던 기억이 나네요.
    ps를 하다 보니 의외로 저런 걸 이용하느 문제가 적잖게 나오는 거 같아요..

    답글삭제