K - 商店街の区画選び / Choosing Blocks in a Shopping Street 解説 by seekworser

平方分割の実装について

公式解説(kyopro_friends)さんの解説と同じ解法ですが、平方分割の実装について(この問題に限らず)より自分が書きやすいと思っている方法を紹介します。

一般に長さ \(B\) のバケットに分けられたデータ構造に対して \([l, r)\) クエリを処理する際には \(l\)\(r\) 未満である間、以下のように処理を行うことにすると考えるとよいです。

  1. \(l \bmod B \equiv 0\) かつ \(l + B \le r\) の場合、バケット全体に処理を行い、\(l\)\(B\) を加えます。
  2. そうではない場合、\(l\) 番目のインデックスに処理を行い \(l\)\(1\) を加えます。

この実装の良い点として、全体を処理するべきバケットが何番目か、左端・右端へのはみ出しがあるかなどの細部に関わらず、現在の \(l\) の値のみによって処理を決定できるため、実装の際に混乱を招きにくいと考えています。 実装例を下記に示します。(43行目から52行目の while ループが上記の処理になります。)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (ll i=0; i<(n); i++)
#define all(a) a.begin(), a.end()
template<typename T> bool chmin(T &a, T b) {if (a > b){a = b; return true;} return false;}
template<typename T> bool chmax(T &a, T b) {if (a < b){a = b; return true;} return false;}
const ll INFL = (1LL << 61);

int main() {
    ll n,k,q; cin >> n >> k >> q;
    vector<ll> a(n), c(n);
    rep(i, n) cin >> a[i];
    rep(i, n) cin >> c[i];
    vector<ll> acum(n+1, 0), ccum(n+1, 0);
    rep(i, n) {
        acum[i+1] = acum[i] + a[i];
        ccum[i+1] = ccum[i] + c[i];
    }
    vector<ll> val(n - k + 1), cnt(n - k + 1);
    ll cmax = 0;
    rep(i, n - k + 1) {
        val[i] = acum[i+k] - acum[i];
        cnt[i] = ccum[i+k] - ccum[i];
        chmax(cmax, cnt[i]);
    }
    ll b = 300;
    ll block = (n - k + 1 + b - 1) / b;
    vector<ll> block_cntmin(block, INFL);
    rep(i, n - k + 1) chmin(block_cntmin[i / b], cnt[i]);
    vector block_valmin(block, vector<ll>(b, INFL));
    rep(i, n - k + 1) { chmin(block_valmin[i / b][cnt[i] - block_cntmin[i / b]], val[i]); }
    rep(i, block) for (ll j=b-2; j>=0; j--) chmin(block_valmin[i][j], block_valmin[i][j+1]);
    vector<ll> lazy(block, 0);

    rep(i, q) {
        ll t,x,y; cin >> t >> x >> y;
        if (t == 1) {
            --x;
            vector<ll> rebuild_block;
            ll l = max(0LL, x - k + 1);
            ll r = min(n - k + 1, x + 1);
            while (l < r) {
                if (l % b == 0 && l + b <= r) {
                    lazy[l / b] += y - a[x];
                    l += b;
                } else {
                    if (rebuild_block.size() == 0 || rebuild_block.back() != l / b) rebuild_block.emplace_back(l / b);
                    val[l] += y - a[x];
                    l++;
                }
            }
            for (auto i : rebuild_block) {
                block_valmin[i] = vector<ll>(b, INFL);
                rep(j, b) {
                    if (i*b+j >= n - k + 1) break;
                    chmin(block_valmin[i][cnt[i*b+j] - block_cntmin[i]], val[i*b+j]);
                }
                for (ll j=b-2; j>=0; j--) chmin(block_valmin[i][j], block_valmin[i][j+1]);
            }
            a[x] = y;
        } else {
            if (cmax < x) {cout << "IMPOSSIBLE\n"; continue;}
            ll ans = INFL;
            rep(i, block) {
                if (block_cntmin[i] + b <= x) continue;
                ll idx = max(0LL, x - block_cntmin[i]);
                chmin(ans, block_valmin[i][idx] + lazy[i]);
            }
            cout << ans << "\n";
        }
    }
}

投稿日時:
最終更新: