Skip to main content
  1. Posts/
  2. Algorithm/

BOJ 18830 하이퍼 수열과 하이퍼 쿼리

·387 words·2 mins
Jiho Kim
Author
Jiho Kim
달려 또 달려

📝 문제 정보
#

🧐 관찰 및 접근
#

  • 11차원인게 조금 이슈이므로, 간단하게 3차원정도로 생각해보자.
    • 2차원이면 그냥 누적합 배열에서 $S(a_2, b_2) - S(a_1, b_2) - S(a_2, b_1) + S(a_1, b_1)$이었던 것이 기억난다.
    • $a_1, b_1, c_1, a_2, b_2, c_2$가 주어진다.
    • $a_1 \leq \alpha \leq a_2, b_1 \leq \beta \leq b_2, c_1 \leq \gamma \leq c_2$ 인 어쩌구에 대해 합을 출력한다.
    • 이를 누적합으로 생각하면, $S(a_2, b_2, c_2) - S(a_1, b_2, c_2) - S(a_2, b_1, c_2) - S(a_2, b_2, c_1) + S(a_1, b_1, c_2) + S(a_1, b_2, c_1) + S(a_2, b_1, c_1) - S(a_1, b_1, c_1)$ 인 것 같다! 아님말고
    • 암튼 $n$차원일때 누적합 배열을 구해둔다면 $2^n$의 시간복잡도로 계산할 수 있는 것 같다.
    • 쿼리가 4만개이므로 $40000 * 2048 = 81920000$이므로 충분히 돌 것 같다.
    • 그런데 누적합 배열을 만드는게 $O(2^n * mno...w)$ 라서 이게 20억인데…
      • 포함배제로 구하지 말고, 차원에 대해서 구하고 밀어버리자.

💻 풀이
#

  • 코드 (C++):
const int cdim = 11;
using v11 = array<int, cdim>;
v11 dim, sz;
int totSz;
vector<ll> A, pfsum;

int point_to_idx(const v11 &point){
    int idx = 0;
    rep(d, 0, cdim) idx += point[d] * sz[d];
    return idx;
}

vector<ll> make_prefix_sum(int cd = 0, v11 cp = {}){
    vector<ll> res;
    if(cd == cdim){
        int idx = point_to_idx(cp);
        res.push_back(A[idx]);
        return res;
    }

    rep(i, 0, dim[cd]){
        cp[cd] = i;
        vector<ll> tmp = make_prefix_sum(cd+1, cp);
        if(res.empty()) res = tmp;
        else{
            rep(j, 0, tmp.size()) res.push_back(res[(i-1) * sz[cd] + j] + tmp[j]);
        }
    }
    cp[cd] = 0;

    return res;
}

void solve(){
    rep(i, 0, cdim) cin >> dim[i];
    sz[cdim-1] = 1;
    rrep(d, cdim-1, 0) sz[d] = sz[d+1] * dim[d+1];
    totSz = sz[0] * dim[0];
    A.resize(totSz);
    pfsum.resize(totSz, 0);
    rep(i, 0, totSz) cin >> A[i];

    pfsum = make_prefix_sum();

    int Q; cin >> Q;
    while(Q--){
        v11 L, R;
        rep(d, 0, cdim) cin >> L[d];
        rep(d, 0, cdim) cin >> R[d];
        rep(d, 0, cdim) L[d]--, R[d]--;

        ll ans = 0;
        rep(mask, 0, (1<<cdim)){
            v11 point = R;
            bool add = true, flag = true;

            rep(d, 0, cdim) if(mask & (1<<d)){
                point[d] = L[d]-1;
                add = !add;
                if(point[d] < 0){
                    flag = false;
                    break;
                }
            }
            if(!flag) continue;

            int idx = point_to_idx(point);
            if(add) ans += pfsum[idx];
            else ans -= pfsum[idx];
        }
        cout << ans << "\n";
    }
}
🔒

구현 코드 잠금

아래 쿠팡 링크를 방문하시면 코드가 공개됩니다.
광고 수익이 블로그 운영에 도움이 됩니다 🙏

🛒 쿠팡 방문하고 코드 보기

방문 후 잠금이 자동으로 해제됩니다